From 50acd3d8d9c1434974a4c79a1b10d7b98c87b007 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sat, 9 Oct 2010 11:36:22 +0200 Subject: [PATCH 001/136] 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: From 09209056fe69fac366a431c9c79ccc1d04fa2db7 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sat, 9 Oct 2010 11:42:32 +0200 Subject: [PATCH 002/136] Fix some bad meta-data of the French localization of ChannelStats --- plugins/ChannelStats/locale/fr.po | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/ChannelStats/locale/fr.po b/plugins/ChannelStats/locale/fr.po index d461f5f24..d06f191a5 100644 --- a/plugins/ChannelStats/locale/fr.po +++ b/plugins/ChannelStats/locale/fr.po @@ -1,12 +1,12 @@ -# French translations for supybot-i package -# Traductions françaises du paquet supybot-i. -# Copyright (C) 2010 THE supybot-i'S COPYRIGHT HOLDER +# French translations for supybot-i18n package +# Traductions françaises du paquet supybot-i18n. +# Copyright (C) 2010 THE supybot-i18n'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" +"Project-Id-Version: supybot-i18n\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" From 842221801af64473b18a794a3679dc2f9811599c Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sun, 10 Oct 2010 14:45:07 +0200 Subject: [PATCH 003/136] Fix the internationalisation problem for the docstrings --- .../{ChannelStats.pot => messages.pot} | 247 +++++++++++++----- 1 file changed, 181 insertions(+), 66 deletions(-) rename plugins/ChannelStats/{ChannelStats.pot => messages.pot} (57%) diff --git a/plugins/ChannelStats/ChannelStats.pot b/plugins/ChannelStats/messages.pot similarity index 57% rename from plugins/ChannelStats/ChannelStats.pot rename to plugins/ChannelStats/messages.pot index d5f4c7797..984790c1a 100644 --- a/plugins/ChannelStats/ChannelStats.pot +++ b/plugins/ChannelStats/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-10-06 16:16+0200\n" +"POT-Creation-Date: 2010-10-09 17:55+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,52 +17,85 @@ msgstr "" "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." +#: plugin.py:48 +msgid "ChannelStats" 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." +#: plugin.py:51 +msgid "actions" msgstr "" -#: plugin.py:272 -msgid "character" +#: plugin.py:51 +msgid "chars" msgstr "" -#: plugin.py:273 plugin.py:355 -msgid "word" +#: plugin.py:51 plugin.py:83 +msgid "frowns" msgstr "" -#: plugin.py:274 plugin.py:356 -msgid "smiley" +#: plugin.py:51 +msgid "joins" msgstr "" -#: plugin.py:275 plugin.py:357 -msgid "frown" +#: plugin.py:51 +msgid "kicks" msgstr "" -#: plugin.py:277 plugin.py:358 -msgid "was an ACTION" +#: plugin.py:51 +msgid "modes" msgstr "" -#: plugin.py:278 plugin.py:359 -msgid "were ACTIONs" +#: plugin.py:52 +msgid "msgs" msgstr "" -#: plugin.py:280 plugin.py:281 plugin.py:282 plugin.py:283 plugin.py:284 -#: plugin.py:285 plugin.py:286 -msgid "time" +#: plugin.py:52 +msgid "parts" msgstr "" -#: plugin.py:289 -#, python-format -msgid "I have no stats for that %s in %s." +#: plugin.py:52 +msgid "quits" msgstr "" -#: plugin.py:291 +#: plugin.py:52 plugin.py:84 +msgid "smileys" +msgstr "" + +#: plugin.py:52 +msgid "topics" +msgstr "" + +#: plugin.py:52 +msgid "words" +msgstr "" + +#: plugin.py:52 +msgid "users" +msgstr "" + +#: plugin.py:121 +msgid "kicked" +msgstr "" + +#: plugin.py:139 plugin.py:147 plugin.py:148 plugin.py:149 plugin.py:160 +#: plugin.py:206 plugin.py:207 plugin.py:208 plugin.py:210 plugin.py:225 +#: plugin.py:226 plugin.py:227 +msgid "channelStats" +msgstr "" + +#: plugin.py:165 +msgid "ChannelStats.db" +msgstr "" + +#: plugin.py:195 +msgid "PRIVMSG" +msgstr "" + +#: plugin.py:197 +msgid "selfStats" +msgstr "" + +#: plugin.py:246 msgid "" "[] []\n" "\n" @@ -73,21 +106,68 @@ msgid "" " " msgstr "" -#: plugin.py:305 +#: plugin.py:259 +msgid "I couldn't find you in my user database." +msgstr "" + +#: plugin.py:272 msgid "" -"There's really no reason why you should have underscores or brackets in your " -"mathematical expression. Please remove them." +"%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:309 -msgid "You can't use lambda in this command." +#: plugin.py:278 +msgid "message" msgstr "" -#: plugin.py:323 -msgid "stat variable" +#: plugin.py:279 +msgid "character" msgstr "" -#: plugin.py:335 +#: plugin.py:280 plugin.py:365 +msgid "word" +msgstr "" + +#: plugin.py:281 plugin.py:366 +msgid "smiley" +msgstr "" + +#: plugin.py:282 plugin.py:367 +msgid "frown" +msgstr "" + +#: plugin.py:284 plugin.py:368 +msgid "was an ACTION" +msgstr "" + +#: plugin.py:285 plugin.py:369 +msgid "were ACTIONs" +msgstr "" + +#: plugin.py:287 plugin.py:288 plugin.py:289 plugin.py:290 plugin.py:291 +#: plugin.py:292 plugin.py:293 +msgid "time" +msgstr "" + +#: plugin.py:296 +#, python-format +msgid "I have no stats for that %s in %s." +msgstr "" + +#: plugin.py:298 plugin.py:346 plugin.py:382 +msgid "channeldb" +msgstr "" + +#: plugin.py:298 +msgid "something" +msgstr "" + +#: plugin.py:300 +msgid "__builtins__" +msgstr "" + +#: plugin.py:305 msgid "" "[] \n" "\n" @@ -100,51 +180,42 @@ msgid "" " " msgstr "" -#: plugin.py:349 +#: plugin.py:315 +msgid "_[]" +msgstr "" + +#: plugin.py:316 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." +"There's really no reason why you should have underscores or brackets in your " +"mathematical expression. Please remove them." msgstr "" -#: plugin.py:360 -msgid "join" +#: plugin.py:319 +msgid "lambda" msgstr "" -#: plugin.py:361 -msgid "part" +#: plugin.py:320 +msgid "You can't use lambda in this command." msgstr "" -#: plugin.py:362 -msgid "quit" +#: plugin.py:332 +msgid "inf" msgstr "" -#: plugin.py:363 -msgid "kick" +#: plugin.py:334 +msgid "stat variable" 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 +#: plugin.py:343 #, python-format -msgid "I've never been on %s." +msgid "#%s %s (%.3g)" msgstr "" -#: plugin.py:372 +#: plugin.py:346 +msgid "text" +msgstr "" + +#: plugin.py:351 msgid "" "[]\n" "\n" @@ -153,3 +224,47 @@ msgid "" " the message isn't sent on the channel itself.\n" " " msgstr "" + +#: plugin.py:359 +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:370 +msgid "join" +msgstr "" + +#: plugin.py:371 +msgid "part" +msgstr "" + +#: plugin.py:372 +msgid "quit" +msgstr "" + +#: plugin.py:373 +msgid "kick" +msgstr "" + +#: plugin.py:374 +msgid "mode" +msgstr "" + +#: plugin.py:374 plugin.py:375 +msgid "change" +msgstr "" + +#: plugin.py:375 +msgid "topic" +msgstr "" + +#: plugin.py:377 plugin.py:378 +msgid "user" +msgstr "" + +#: plugin.py:381 +#, python-format +msgid "I've never been on %s." +msgstr "" From a750fe6a2ed5173866f0ca43c8a9cdb12cf05cc8 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sun, 10 Oct 2010 14:45:25 +0200 Subject: [PATCH 004/136] Fix the internationalisation problem for the docstrings --- plugins/ChannelStats/locale/fr.po | 218 +++++++++++++++--------------- plugins/ChannelStats/messages.pot | 190 +++++--------------------- plugins/ChannelStats/plugin.py | 45 +++--- src/i18n.py | 68 +++++++--- 4 files changed, 214 insertions(+), 307 deletions(-) diff --git a/plugins/ChannelStats/locale/fr.po b/plugins/ChannelStats/locale/fr.po index d06f191a5..e25cfbc02 100644 --- a/plugins/ChannelStats/locale/fr.po +++ b/plugins/ChannelStats/locale/fr.po @@ -8,9 +8,9 @@ msgid "" msgstr "" "Project-Id-Version: supybot-i18n\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" +"POT-Creation-Date: 2010-10-10 09:35+CEST\n" +"PO-Revision-Date: 2010-10-10 10:08+0100\n" +"Last-Translator: Valentin Lorentz \n" "Language-Team: French\n" "Language: fr\n" "MIME-Version: 1.0\n" @@ -18,59 +18,7 @@ msgstr "" "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 +#: plugin.py:246 msgid "" "[] []\n" "\n" @@ -79,23 +27,62 @@ msgid "" " isn't given, it defaults to the user sending the command.\n" " " msgstr "" -"[canal>] [nom]\n" +"[] [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:259 +msgid "I couldn't find you in my user database." +msgstr "Je ne peux vous trouver dans ma base de données." -#: plugin.py:309 -msgid "You can't use lambda in this command." -msgstr "Vous ne pouvez utiliser lambda dans cette commande." +#: plugin.py:272 +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:323 -msgid "stat variable" -msgstr "Variable statistique" +#: plugin.py:279 +msgid "character" +msgstr "caractère" -#: plugin.py:335 +#: plugin.py:280 +#: plugin.py:363 +msgid "word" +msgstr "mot" + +#: plugin.py:281 +#: plugin.py:364 +msgid "smiley" +msgstr "smiley" + +#: plugin.py:282 +#: plugin.py:365 +msgid "frown" +msgstr "sadley" + +#: plugin.py:284 +#: plugin.py:366 +msgid "was an ACTION" +msgstr "était une action" + +#: plugin.py:285 +#: plugin.py:367 +msgid "were ACTIONs" +msgstr "étaient des ACTIONs" + +#: plugin.py:287 +#: plugin.py:288 +#: plugin.py:289 +#: plugin.py:290 +#: plugin.py:291 +#: plugin.py:292 +#: plugin.py:293 +msgid "time" +msgstr " fois" + +#: plugin.py:296 +msgid "I have no stats for that %s in %s." +msgstr "Je n'ai pas de statistiques pour %s sur %s." + +#: plugin.py:304 msgid "" "[] \n" "\n" @@ -110,50 +97,19 @@ msgstr "" "\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:315 +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:319 +msgid "You can't use lambda in this command." +msgstr "Vous ne pouvez utiliser lambda dans cette commande." + +#: plugin.py:333 +msgid "stat variable" +msgstr "Variable statistique" + #: 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" @@ -165,3 +121,45 @@ msgstr "" "\n" "Retourne les statistiques pour le . n'est nécessaire que si le message n'est pas envoyé sur le canal lui-même." +#: plugin.py:357 +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:368 +msgid "join" +msgstr "arrivée" + +#: plugin.py:369 +msgid "part" +msgstr "départ" + +#: plugin.py:370 +msgid "quit" +msgstr "quit" + +#: plugin.py:371 +msgid "kick" +msgstr "kick" + +#: plugin.py:372 +msgid "mode" +msgstr "mode" + +#: plugin.py:372 +#: plugin.py:373 +msgid "change" +msgstr "changement" + +#: plugin.py:373 +msgid "topic" +msgstr "topic" + +#: plugin.py:375 +#: plugin.py:376 +msgid "user" +msgstr "utilisateur" + +#: plugin.py:379 +msgid "I've never been on %s." +msgstr "Je n'ai jamais été sur %s." + diff --git a/plugins/ChannelStats/messages.pot b/plugins/ChannelStats/messages.pot index 984790c1a..011c2b4c5 100644 --- a/plugins/ChannelStats/messages.pot +++ b/plugins/ChannelStats/messages.pot @@ -1,107 +1,27 @@ # SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. +# Copyright (C) YEAR ORGANIZATION # FIRST AUTHOR , YEAR. # -#, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-10-09 17:55+0200\n" +"POT-Creation-Date: 2010-10-10 09:35+CEST\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" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" -#: plugin.py:48 -msgid "ChannelStats" -msgstr "" - -#: plugin.py:51 -msgid "actions" -msgstr "" - -#: plugin.py:51 -msgid "chars" -msgstr "" - -#: plugin.py:51 plugin.py:83 -msgid "frowns" -msgstr "" - -#: plugin.py:51 -msgid "joins" -msgstr "" - -#: plugin.py:51 -msgid "kicks" -msgstr "" - -#: plugin.py:51 -msgid "modes" -msgstr "" - -#: plugin.py:52 -msgid "msgs" -msgstr "" - -#: plugin.py:52 -msgid "parts" -msgstr "" - -#: plugin.py:52 -msgid "quits" -msgstr "" - -#: plugin.py:52 plugin.py:84 -msgid "smileys" -msgstr "" - -#: plugin.py:52 -msgid "topics" -msgstr "" - -#: plugin.py:52 -msgid "words" -msgstr "" - -#: plugin.py:52 -msgid "users" -msgstr "" - -#: plugin.py:121 -msgid "kicked" -msgstr "" - -#: plugin.py:139 plugin.py:147 plugin.py:148 plugin.py:149 plugin.py:160 -#: plugin.py:206 plugin.py:207 plugin.py:208 plugin.py:210 plugin.py:225 -#: plugin.py:226 plugin.py:227 -msgid "channelStats" -msgstr "" - -#: plugin.py:165 -msgid "ChannelStats.db" -msgstr "" - -#: plugin.py:195 -msgid "PRIVMSG" -msgstr "" - -#: plugin.py:197 -msgid "selfStats" -msgstr "" #: plugin.py:246 +#, docstring msgid "" "[] []\n" "\n" " Returns the statistics for on . is only\n" -" necessary if the message isn't sent on the channel itself. If " -"\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 "" @@ -111,37 +31,30 @@ msgid "I couldn't find you in my user database." msgstr "" #: plugin.py:272 -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:278 -msgid "message" +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:279 msgid "character" msgstr "" -#: plugin.py:280 plugin.py:365 +#: plugin.py:280 plugin.py:363 msgid "word" msgstr "" -#: plugin.py:281 plugin.py:366 +#: plugin.py:281 plugin.py:364 msgid "smiley" msgstr "" -#: plugin.py:282 plugin.py:367 +#: plugin.py:282 plugin.py:365 msgid "frown" msgstr "" -#: plugin.py:284 plugin.py:368 +#: plugin.py:284 plugin.py:366 msgid "was an ACTION" msgstr "" -#: plugin.py:285 plugin.py:369 +#: plugin.py:285 plugin.py:367 msgid "were ACTIONs" msgstr "" @@ -151,28 +64,15 @@ msgid "time" msgstr "" #: plugin.py:296 -#, python-format msgid "I have no stats for that %s in %s." msgstr "" -#: plugin.py:298 plugin.py:346 plugin.py:382 -msgid "channeldb" -msgstr "" - -#: plugin.py:298 -msgid "something" -msgstr "" - -#: plugin.py:300 -msgid "__builtins__" -msgstr "" - -#: plugin.py:305 +#: plugin.py:304 +#, docstring msgid "" "[] \n" "\n" -" Returns the ranking of users according to the given stat " -"expression.\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" @@ -181,90 +81,64 @@ msgid "" msgstr "" #: plugin.py:315 -msgid "_[]" -msgstr "" - -#: plugin.py:316 -msgid "" -"There's really no reason why you should have underscores or brackets in your " -"mathematical expression. Please remove them." +msgid "There's really no reason why you should have underscores or brackets in your mathematical expression. Please remove them." msgstr "" #: plugin.py:319 -msgid "lambda" -msgstr "" - -#: plugin.py:320 msgid "You can't use lambda in this command." msgstr "" -#: plugin.py:332 -msgid "inf" -msgstr "" - -#: plugin.py:334 +#: plugin.py:333 msgid "stat variable" msgstr "" -#: plugin.py:343 -#, python-format -msgid "#%s %s (%.3g)" -msgstr "" - -#: plugin.py:346 -msgid "text" -msgstr "" - -#: plugin.py:351 +#: plugin.py:349 +#, docstring msgid "" "[]\n" "\n" -" Returns the statistics for . is only necessary " -"if\n" +" Returns the statistics for . is only necessary if\n" " the message isn't sent on the channel itself.\n" " " msgstr "" -#: plugin.py:359 -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." +#: plugin.py:357 +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:370 +#: plugin.py:368 msgid "join" msgstr "" -#: plugin.py:371 +#: plugin.py:369 msgid "part" msgstr "" -#: plugin.py:372 +#: plugin.py:370 msgid "quit" msgstr "" -#: plugin.py:373 +#: plugin.py:371 msgid "kick" msgstr "" -#: plugin.py:374 +#: plugin.py:372 msgid "mode" msgstr "" -#: plugin.py:374 plugin.py:375 +#: plugin.py:372 plugin.py:373 msgid "change" msgstr "" -#: plugin.py:375 +#: plugin.py:373 msgid "topic" msgstr "" -#: plugin.py:377 plugin.py:378 +#: plugin.py:375 plugin.py:376 msgid "user" msgstr "" -#: plugin.py:381 -#, python-format +#: plugin.py:379 msgid "I've never been on %s." msgstr "" + diff --git a/plugins/ChannelStats/plugin.py b/plugins/ChannelStats/plugin.py index 9a91b3056..0f0e3482b 100644 --- a/plugins/ChannelStats/plugin.py +++ b/plugins/ChannelStats/plugin.py @@ -34,7 +34,7 @@ import types import supybot.log as log import supybot.conf as conf -import supybot.i18n as i18n +from supybot.i18n import PluginInternationalization, internationalizeDocstring import supybot.utils as utils import supybot.world as world import supybot.ircdb as ircdb @@ -45,7 +45,7 @@ import supybot.plugins as plugins import supybot.ircutils as ircutils import supybot.callbacks as callbacks -_ = i18n.PluginInternationalization('ChannelStats') +_ = PluginInternationalization('ChannelStats') class ChannelStat(irclib.IrcCommandDispatcher): _values = ['actions', 'chars', 'frowns', 'joins', 'kicks','modes', @@ -241,7 +241,14 @@ class ChannelStats(callbacks.Plugin): self.db[channel, id] = UserStat() self.db.channels[channel][id].kicked += 1 + @internationalizeDocstring 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: @@ -288,17 +295,20 @@ class ChannelStats(callbacks.Plugin): except KeyError: 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__) + @internationalizeDocstring 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, '_[]'): @@ -332,17 +342,15 @@ 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']) + @internationalizeDocstring 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) @@ -369,11 +377,6 @@ class ChannelStats(callbacks.Plugin): irc.reply(s) except KeyError: 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/src/i18n.py b/src/i18n.py index b1a686f92..a94116499 100644 --- a/src/i18n.py +++ b/src/i18n.py @@ -33,6 +33,7 @@ Supybot internationalisation and localisation managment. __all__ = ['PluginInternationalization'] +import re import sys import supybot.conf as conf @@ -44,38 +45,50 @@ 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.""")) +conf.registerGlobalValue(conf.supybot, 'language', + conf.registry.String('en', """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 + if filename.endswith(".pyc"): + filename = filename[0:-1] + + allowed_files = ['__init__.py', 'config.py', 'plugin.py', 'test.py'] + for allowed_file in allowed_files: + if filename.endswith(allowed_file): + return filename[0:-len(allowed_file)] + return i18nClasses = {} +internationalizedCommands = {} + +def reloadLocals(): + for pluginName in i18nClasses: + i18nClasses[pluginName].loadLocale() + for commandHash in internationalizedCommands: + internationalizeDocstring(internationalizedCommands[commandHash]) class PluginInternationalization: """Internationalization managment for a plugin.""" def __init__(self, name='supybot'): self.name = name - self.load_locale('toto') + self.loadLocale() i18nClasses.update({name: self}) - def load_locale(self, locale_name): - directory = get_plugin_dir(self.name) + 'locale/' + def loadLocale(self, localeName=None): + if localeName is None: + localeName = conf.supybot.language() + self.currentLocaleName = localeName + directory = get_plugin_dir(self.name) + 'locale' try: - translation_file = open('%s%s.po' % (directory, locale_name), 'ru') + translationFile = open('%s/%s.po' % (directory, localeName), 'ru') except IOError: # The translation is unavailable self.translations = {} return step = WAITING_FOR_MSGID self.translations = {} - for line in translation_file: + for line in translationFile: line = line[0:-1] # Remove the ending \n if line.startswith(MSGID): @@ -102,22 +115,33 @@ class PluginInternationalization: if step is WAITING_FOR_MSGSTR and line.startswith(MSGSTR): data = line[len(MSGSTR):-1] if len(data) == 0: # Multiline mode - step = IN_MSGID + step = IN_MSGSTR else: - self.translations.update({untranslated: data}) + self._translate(untranslated, data) step = WAITING_FOR_MSGID elif step is IN_MSGSTR and line.startswith('"') and \ line.endswith('"'): - untranslated += line[1:-1] + translated += 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}) + self._translate(untranslated, translated) + + def _translate(self, untranslated, translated): + print repr({self._parse(untranslated): self._parse(translated)}) + self.translations.update({self._parse(untranslated): + self._parse(translated)}) + + def _parse(self, string): + return str.replace(string, '\\n', '\n') # Replace \\n by \n def __call__(self, untranslated, *args): + if self.currentLocaleName != conf.supybot.language(): + # If the locale has been changed + reloadLocals() if len(args) == 0: try: return self.translations[untranslated] @@ -127,3 +151,11 @@ class PluginInternationalization: translation = self(untranslated) return translation % args + +def internationalizeDocstring(obj): + # FIXME: check if the plugin has an _ object + internationalizedCommands.update({hash(obj): obj}) + print "----doc : " + repr(obj.__doc__) + print "----strings : " + repr(sys.modules[obj.__module__]._.translations.keys()) + obj.__doc__=sys.modules[obj.__module__]._(obj.__doc__) + return obj From a8319d3c6ff92d187b3ee79ca22c5158c3c97373 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sun, 10 Oct 2010 15:11:41 +0200 Subject: [PATCH 005/136] Remove debug message and internationalize config.py in ChannelStats --- plugins/ChannelStats/config.py | 15 ++-- plugins/ChannelStats/messages.pot | 144 ------------------------------ src/i18n.py | 2 - 3 files changed, 9 insertions(+), 152 deletions(-) delete mode 100644 plugins/ChannelStats/messages.pot diff --git a/plugins/ChannelStats/config.py b/plugins/ChannelStats/config.py index 3e896e999..d8263bf26 100644 --- a/plugins/ChannelStats/config.py +++ b/plugins/ChannelStats/config.py @@ -31,6 +31,9 @@ import re import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring + +_ = PluginInternationalization('ChannelStats') def configure(advanced): # This will be called by supybot to configure this module. advanced is @@ -54,16 +57,16 @@ class Smileys(registry.Value): ChannelStats = conf.registerPlugin('ChannelStats') conf.registerChannelValue(ChannelStats, 'selfStats', - registry.Boolean(True, """Determines whether the bot will keep channel + registry.Boolean(True, _("""Determines whether the bot will keep channel statistics on itself, possibly skewing the channel stats (especially in - cases where the bot is relaying between channels on a network).""")) + cases where the bot is relaying between channels on a network)."""))) conf.registerChannelValue(ChannelStats, 'smileys', - Smileys(':) ;) ;] :-) :-D :D :P :p (= =)'.split(), """Determines what + Smileys(':) ;) ;] :-) :-D :D :P :p (= =)'.split(), _("""Determines what words (i.e., pieces of text with no spaces in them) are considered - 'smileys' for the purposes of stats-keeping.""")) + 'smileys' for the purposes of stats-keeping."""))) conf.registerChannelValue(ChannelStats, 'frowns', - Smileys(':| :-/ :-\\ :\\ :/ :( :-( :\'('.split(), """Determines what words + Smileys(':| :-/ :-\\ :\\ :/ :( :-( :\'('.split(), _("""Determines what words (i.e., pieces of text with no spaces in them ) are considered 'frowns' for - the purposes of stats-keeping.""")) + the purposes of stats-keeping."""))) # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/ChannelStats/messages.pot b/plugins/ChannelStats/messages.pot deleted file mode 100644 index 011c2b4c5..000000000 --- a/plugins/ChannelStats/messages.pot +++ /dev/null @@ -1,144 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR ORGANIZATION -# FIRST AUTHOR , YEAR. -# -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2010-10-10 09:35+CEST\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=CHARSET\n" -"Content-Transfer-Encoding: ENCODING\n" -"Generated-By: pygettext.py 1.5\n" - - -#: plugin.py:246 -#, docstring -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:259 -msgid "I couldn't find you in my user database." -msgstr "" - -#: plugin.py:272 -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:279 -msgid "character" -msgstr "" - -#: plugin.py:280 plugin.py:363 -msgid "word" -msgstr "" - -#: plugin.py:281 plugin.py:364 -msgid "smiley" -msgstr "" - -#: plugin.py:282 plugin.py:365 -msgid "frown" -msgstr "" - -#: plugin.py:284 plugin.py:366 -msgid "was an ACTION" -msgstr "" - -#: plugin.py:285 plugin.py:367 -msgid "were ACTIONs" -msgstr "" - -#: plugin.py:287 plugin.py:288 plugin.py:289 plugin.py:290 plugin.py:291 -#: plugin.py:292 plugin.py:293 -msgid "time" -msgstr "" - -#: plugin.py:296 -msgid "I have no stats for that %s in %s." -msgstr "" - -#: plugin.py:304 -#, docstring -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:315 -msgid "There's really no reason why you should have underscores or brackets in your mathematical expression. Please remove them." -msgstr "" - -#: plugin.py:319 -msgid "You can't use lambda in this command." -msgstr "" - -#: plugin.py:333 -msgid "stat variable" -msgstr "" - -#: plugin.py:349 -#, docstring -msgid "" -"[]\n" -"\n" -" Returns the statistics for . is only necessary if\n" -" the message isn't sent on the channel itself.\n" -" " -msgstr "" - -#: plugin.py:357 -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:368 -msgid "join" -msgstr "" - -#: plugin.py:369 -msgid "part" -msgstr "" - -#: plugin.py:370 -msgid "quit" -msgstr "" - -#: plugin.py:371 -msgid "kick" -msgstr "" - -#: plugin.py:372 -msgid "mode" -msgstr "" - -#: plugin.py:372 plugin.py:373 -msgid "change" -msgstr "" - -#: plugin.py:373 -msgid "topic" -msgstr "" - -#: plugin.py:375 plugin.py:376 -msgid "user" -msgstr "" - -#: plugin.py:379 -msgid "I've never been on %s." -msgstr "" - diff --git a/src/i18n.py b/src/i18n.py index a94116499..2500d7767 100644 --- a/src/i18n.py +++ b/src/i18n.py @@ -155,7 +155,5 @@ class PluginInternationalization: def internationalizeDocstring(obj): # FIXME: check if the plugin has an _ object internationalizedCommands.update({hash(obj): obj}) - print "----doc : " + repr(obj.__doc__) - print "----strings : " + repr(sys.modules[obj.__module__]._.translations.keys()) obj.__doc__=sys.modules[obj.__module__]._(obj.__doc__) return obj From 4552038be4aee0619083311f4c676d687e21f2be Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sun, 10 Oct 2010 15:12:06 +0200 Subject: [PATCH 006/136] Renamed the .pot file of ChannelStats --- plugins/ChannelStats/ocstrings.pot | 131 +++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 plugins/ChannelStats/ocstrings.pot diff --git a/plugins/ChannelStats/ocstrings.pot b/plugins/ChannelStats/ocstrings.pot new file mode 100644 index 000000000..16fa3dc92 --- /dev/null +++ b/plugins/ChannelStats/ocstrings.pot @@ -0,0 +1,131 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-10 15:07+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: config.py:57 +msgid "" +"Determines whether the bot will keep channel\n" +" statistics on itself, possibly skewing the channel stats (especially in\n" +" cases where the bot is relaying between channels on a network)." +msgstr "" + +#: config.py:61 +msgid "" +"Determines what\n" +" words (i.e., pieces of text with no spaces in them) are considered\n" +" 'smileys' for the purposes of stats-keeping." +msgstr "" + +#: config.py:65 +msgid "" +"Determines what words\n" +" (i.e., pieces of text with no spaces in them ) are considered 'frowns' for\n" +" the purposes of stats-keeping." +msgstr "" + +#: plugin.py:259 +msgid "I couldn't find you in my user database." +msgstr "" + +#: plugin.py:272 +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:279 +msgid "character" +msgstr "" + +#: plugin.py:280 plugin.py:363 +msgid "word" +msgstr "" + +#: plugin.py:281 plugin.py:364 +msgid "smiley" +msgstr "" + +#: plugin.py:282 plugin.py:365 +msgid "frown" +msgstr "" + +#: plugin.py:284 plugin.py:366 +msgid "was an ACTION" +msgstr "" + +#: plugin.py:285 plugin.py:367 +msgid "were ACTIONs" +msgstr "" + +#: plugin.py:287 plugin.py:288 plugin.py:289 plugin.py:290 plugin.py:291 +#: plugin.py:292 plugin.py:293 +msgid "time" +msgstr "" + +#: plugin.py:296 +msgid "I have no stats for that %s in %s." +msgstr "" + +#: plugin.py:315 +msgid "There's really no reason why you should have underscores or brackets in your mathematical expression. Please remove them." +msgstr "" + +#: plugin.py:319 +msgid "You can't use lambda in this command." +msgstr "" + +#: plugin.py:333 +msgid "stat variable" +msgstr "" + +#: plugin.py:357 +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:368 +msgid "join" +msgstr "" + +#: plugin.py:369 +msgid "part" +msgstr "" + +#: plugin.py:370 +msgid "quit" +msgstr "" + +#: plugin.py:371 +msgid "kick" +msgstr "" + +#: plugin.py:372 +msgid "mode" +msgstr "" + +#: plugin.py:372 plugin.py:373 +msgid "change" +msgstr "" + +#: plugin.py:373 +msgid "topic" +msgstr "" + +#: plugin.py:375 plugin.py:376 +msgid "user" +msgstr "" + +#: plugin.py:379 +msgid "I've never been on %s." +msgstr "" + From 850aa279a4473ba7862a1147a11a322d2ab50474 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sun, 10 Oct 2010 15:43:13 +0200 Subject: [PATCH 007/136] Finish localisation of ChannelStats --- plugins/ChannelStats/locale/fr.po | 111 +++++++++++++++++++----------- 1 file changed, 71 insertions(+), 40 deletions(-) diff --git a/plugins/ChannelStats/locale/fr.po b/plugins/ChannelStats/locale/fr.po index e25cfbc02..df1bd11ad 100644 --- a/plugins/ChannelStats/locale/fr.po +++ b/plugins/ChannelStats/locale/fr.po @@ -8,8 +8,8 @@ msgid "" msgstr "" "Project-Id-Version: supybot-i18n\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-10-10 09:35+CEST\n" -"PO-Revision-Date: 2010-10-10 10:08+0100\n" +"POT-Creation-Date: 2010-10-10 15:07+CEST\n" +"PO-Revision-Date: 2010-10-10 15:42+0100\n" "Last-Translator: Valentin Lorentz \n" "Language-Team: French\n" "Language: fr\n" @@ -18,18 +18,26 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -#: plugin.py:246 +#: config.py:57 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 "" -"[] [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" +"Determines whether the bot will keep channel\n" +" statistics on itself, possibly skewing the channel stats (especially in\n" +" cases where the bot is relaying between channels on a network)." +msgstr "Détermine si le bot se prendre en compte dans les statistiques du canal, ce qui peut les fausser 'particulièrement dans le cas où le bot relaye entre des canaux)" + +#: config.py:61 +msgid "" +"Determines what\n" +" words (i.e., pieces of text with no spaces in them) are considered\n" +" 'smileys' for the purposes of stats-keeping." +msgstr "Détermine quels mots (c'est à dire, les morceaux de texte sans espace) sont considérés comme smileys par les stats." + +#: config.py:65 +msgid "" +"Determines what words\n" +" (i.e., pieces of text with no spaces in them ) are considered 'frowns' for\n" +" the purposes of stats-keeping." +msgstr "Détermine quels mots (c'est à dire, les morceaux de texte sans espace) sont considérés comme sadleys par les stats." #: plugin.py:259 msgid "I couldn't find you in my user database." @@ -82,21 +90,6 @@ msgstr " fois" msgid "I have no stats for that %s in %s." msgstr "Je n'ai pas de statistiques pour %s sur %s." -#: plugin.py:304 -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:315 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." @@ -109,18 +102,6 @@ msgstr "Vous ne pouvez utiliser lambda dans cette commande." msgid "stat variable" msgstr "Variable statistique" -#: plugin.py:349 -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." - #: plugin.py:357 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." @@ -163,3 +144,53 @@ msgstr "utilisateur" msgid "I've never been on %s." msgstr "Je n'ai jamais été sur %s." +#~ 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 "" +#~ "[] [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" +#~ 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." +#~ 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." + From 9a2eea4272a7f427949fe47053b75894222aa002 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sun, 10 Oct 2010 16:34:34 +0200 Subject: [PATCH 008/136] Change the version name --- src/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conf.py b/src/conf.py index 54141d5a8..8cdec893c 100644 --- a/src/conf.py +++ b/src/conf.py @@ -40,7 +40,7 @@ import supybot.ircutils as ircutils ### # version: This should be pretty obvious. ### -version = '0.83.4.1+git' +version = '0.83.4.1+fr+git' ### # *** The following variables are affected by command-line options. They are From b44401dd1c7af8ee63365b50b32ec0fffcb05438 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sun, 10 Oct 2010 16:35:31 +0200 Subject: [PATCH 009/136] Remove a forgotten debug print --- src/i18n.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/i18n.py b/src/i18n.py index 2500d7767..412536dd0 100644 --- a/src/i18n.py +++ b/src/i18n.py @@ -131,7 +131,6 @@ class PluginInternationalization: self._translate(untranslated, translated) def _translate(self, untranslated, translated): - print repr({self._parse(untranslated): self._parse(translated)}) self.translations.update({self._parse(untranslated): self._parse(translated)}) From d156ae7474edbb0a7ab70bbc6adb8605d1643aa8 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sun, 10 Oct 2010 16:46:55 +0200 Subject: [PATCH 010/136] Add the configuration variable help reloading --- src/registry.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/registry.py b/src/registry.py index 5bd5b09b1..5fb9b7f25 100644 --- a/src/registry.py +++ b/src/registry.py @@ -253,6 +253,9 @@ class Group(object): fullname = join(names) node.setName(fullname) else: + # We do this in order to reload the help, if it changed. + if node._help != '' and node._help != self._children[name]._help: + self._children[name]._help = node._help # We do this so the return value from here is at least useful; # otherwise, we're just returning a useless, unattached node # that's simply a waste of space. From 4a5b98507f8e7e1814fc08cc59254946c02010d3 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sun, 10 Oct 2010 16:54:46 +0200 Subject: [PATCH 011/136] Revert 9a2eea4272a7f427949fe47053b75894222aa002 --- src/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conf.py b/src/conf.py index 8cdec893c..54141d5a8 100644 --- a/src/conf.py +++ b/src/conf.py @@ -40,7 +40,7 @@ import supybot.ircutils as ircutils ### # version: This should be pretty obvious. ### -version = '0.83.4.1+fr+git' +version = '0.83.4.1+git' ### # *** The following variables are affected by command-line options. They are From 90a0ea3c3afc37715c146984d948ba93587c03fe Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sun, 10 Oct 2010 18:18:51 +0200 Subject: [PATCH 012/136] Edit scripts/supybot-plugin-create to fit plugin templates changes --- scripts/supybot-plugin-create | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/scripts/supybot-plugin-create b/scripts/supybot-plugin-create index 92aa431ae..5e17a6968 100644 --- a/scripts/supybot-plugin-create +++ b/scripts/supybot-plugin-create @@ -95,6 +95,9 @@ from supybot.commands import * import supybot.plugins as plugins import supybot.ircutils as ircutils import supybot.callbacks as callbacks +from supybot.i18n import PluginInternationalization, internationalizeDocstring + +_ = PluginInternationalization('%s') class %s(callbacks.Plugin): @@ -114,6 +117,9 @@ configTemplate = ''' import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring + +_ = PluginInternationalization('%s') def configure(advanced): # This will be called by supybot to configure this module. advanced is @@ -127,7 +133,7 @@ def configure(advanced): %s = conf.registerPlugin(%r) # This is where your configuration variables (if any) should go. For example: # conf.registerGlobalValue(%s, 'someConfigVariableName', -# registry.Boolean(False, """Help for someConfigVariableName.""")) +# registry.Boolean(False, _("""Help for someConfigVariableName."""))) # vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: @@ -266,9 +272,10 @@ def main(): finally: fd.close() - writeFile('plugin.py', pluginTemplate % (copyright, name, + writeFile('plugin.py', pluginTemplate % (copyright, name, name, name, threaded, name)) - writeFile('config.py', configTemplate % (copyright, name, name, name, name)) + writeFile('config.py', configTemplate % (copyright, name, name, name, name, + name)) writeFile('__init__.py', __init__Template % (copyright, name)) writeFile('test.py', testTemplate % (copyright, name, name)) writeFile('README.txt', readmeTemplate) From 106a527082f2bad8741ccf1e882b77ab8ced4676 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sun, 10 Oct 2010 18:49:21 +0200 Subject: [PATCH 013/136] Decorates plugin class with internationalizeDocstring --- scripts/supybot-plugin-create | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/supybot-plugin-create b/scripts/supybot-plugin-create index 5e17a6968..3cc3c7b54 100644 --- a/scripts/supybot-plugin-create +++ b/scripts/supybot-plugin-create @@ -99,7 +99,7 @@ from supybot.i18n import PluginInternationalization, internationalizeDocstring _ = PluginInternationalization('%s') - +@internationalizeDocstring class %s(callbacks.Plugin): """Add the help for "@plugin help %s" here This should describe *how* to use this plugin.""" From 70af49feb3f67980452ea6a48d41c93b96a3d2ef Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sat, 16 Oct 2010 09:49:04 +0200 Subject: [PATCH 014/136] Changed .pot file name in ChannelStats. --- .../{ocstrings.pot => messages.pot} | 42 +++++++++++++++++-- 1 file changed, 38 insertions(+), 4 deletions(-) rename plugins/ChannelStats/{ocstrings.pot => messages.pot} (71%) diff --git a/plugins/ChannelStats/ocstrings.pot b/plugins/ChannelStats/messages.pot similarity index 71% rename from plugins/ChannelStats/ocstrings.pot rename to plugins/ChannelStats/messages.pot index 16fa3dc92..0c664ce18 100644 --- a/plugins/ChannelStats/ocstrings.pot +++ b/plugins/ChannelStats/messages.pot @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2010-10-10 15:07+CEST\n" +"POT-Creation-Date: 2010-10-16 09:41+CEST\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -15,27 +15,38 @@ msgstr "" "Generated-By: pygettext.py 1.5\n" -#: config.py:57 +#: config.py:60 msgid "" "Determines whether the bot will keep channel\n" " statistics on itself, possibly skewing the channel stats (especially in\n" " cases where the bot is relaying between channels on a network)." msgstr "" -#: config.py:61 +#: config.py:64 msgid "" "Determines what\n" " words (i.e., pieces of text with no spaces in them) are considered\n" " 'smileys' for the purposes of stats-keeping." msgstr "" -#: config.py:65 +#: config.py:68 msgid "" "Determines what words\n" " (i.e., pieces of text with no spaces in them ) are considered 'frowns' for\n" " the purposes of stats-keeping." msgstr "" +#: plugin.py:246 +#, docstring +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:259 msgid "I couldn't find you in my user database." msgstr "" @@ -77,6 +88,19 @@ msgstr "" msgid "I have no stats for that %s in %s." msgstr "" +#: plugin.py:304 +#, docstring +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:315 msgid "There's really no reason why you should have underscores or brackets in your mathematical expression. Please remove them." msgstr "" @@ -89,6 +113,16 @@ msgstr "" msgid "stat variable" msgstr "" +#: plugin.py:349 +#, docstring +msgid "" +"[]\n" +"\n" +" Returns the statistics for . is only necessary if\n" +" the message isn't sent on the channel itself.\n" +" " +msgstr "" + #: plugin.py:357 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 "" From 949d13cdc9ed8db9346315c6ee32966ca33aefae Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sat, 16 Oct 2010 09:50:23 +0200 Subject: [PATCH 015/136] Internationalize Admin --- plugins/Admin/config.py | 2 + plugins/Admin/messages.pot | 187 +++++++++++++++++++++++++++++++++++++ plugins/Admin/plugin.py | 57 ++++++----- 3 files changed, 224 insertions(+), 22 deletions(-) create mode 100644 plugins/Admin/messages.pot diff --git a/plugins/Admin/config.py b/plugins/Admin/config.py index cbc0f6279..c8995d2ce 100644 --- a/plugins/Admin/config.py +++ b/plugins/Admin/config.py @@ -30,6 +30,8 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Admin') def configure(advanced): # This will be called by supybot to configure this module. advanced is diff --git a/plugins/Admin/messages.pot b/plugins/Admin/messages.pot new file mode 100644 index 000000000..e5833f721 --- /dev/null +++ b/plugins/Admin/messages.pot @@ -0,0 +1,187 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-16 09:41+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: plugin.py:53 +#, docstring +msgid "Nick/channel temporarily unavailable." +msgstr "" + +#: plugin.py:71 +msgid "Cannot join %s, it's full." +msgstr "" + +#: plugin.py:79 +msgid "Cannot join %s, I was not invited." +msgstr "" + +#: plugin.py:87 +msgid "Cannot join %s, it's banned me." +msgstr "" + +#: plugin.py:95 +msgid "Cannot join %s, my keyword was wrong." +msgstr "" + +#: plugin.py:103 +msgid "Cannot join %s, I'm not identified with the NickServ." +msgstr "" + +#: plugin.py:133 +#, docstring +msgid "" +" []\n" +"\n" +" Tell the bot to join the given channel. If is given, it is used\n" +" when attempting to join the channel.\n" +" " +msgstr "" + +#: plugin.py:146 +msgid "I'm already too close to maximum number of channels for this network." +msgstr "" + +#: plugin.py:155 +#, docstring +msgid "" +"takes no arguments\n" +"\n" +" Returns the channels the bot is on. Must be given in private, in order\n" +" to protect the secrecy of secret channels.\n" +" " +msgstr "" + +#: plugin.py:165 +msgid "I'm not currently in any channels." +msgstr "" + +#: plugin.py:171 +msgid "My connection is restricted, I can't change nicks." +msgstr "" + +#: plugin.py:178 +msgid "Someone else is already using that nick." +msgstr "" + +#: plugin.py:185 +msgid "That nick is currently banned." +msgstr "" + +#: plugin.py:192 +msgid "I can't change nicks, the server said %q." +msgstr "" + +#: plugin.py:206 +#, docstring +msgid "" +"[]\n" +"\n" +" Changes the bot's nick to . If no nick is given, returns the\n" +" bot's current nick.\n" +" " +msgstr "" + +#: plugin.py:221 +#, docstring +msgid "" +"[] []\n" +"\n" +" Tells the bot to part the list of channels you give it. is\n" +" only necessary if you want the bot to part a channel other than the\n" +" current channel. If is specified, use it as the part\n" +" message.\n" +" " +msgstr "" + +#: plugin.py:239 +msgid "I'm not in %s." +msgstr "" + +#: plugin.py:251 +#, docstring +msgid "" +" \n" +"\n" +" Gives the user specified by (or the user to whom \n" +" currently maps) the specified capability \n" +" " +msgstr "" + +#: plugin.py:271 +msgid "The \"owner\" capability can't be added in thebot. Use the supybot-adduser program (or edit the users.conf file yourself) to add an owner capability." +msgstr "" + +#: plugin.py:282 +msgid "You can't add capabilities you don't have." +msgstr "" + +#: plugin.py:287 +#, docstring +msgid "" +" \n" +"\n" +" Takes from the user specified by (or the user to whom\n" +" currently maps) the specified capability \n" +" " +msgstr "" + +#: plugin.py:299 +msgid "That user doesn't have that capability." +msgstr "" + +#: plugin.py:301 +msgid "You can't remove capabilities you don't have." +msgstr "" + +#: plugin.py:309 +#, docstring +msgid "" +" []\n" +"\n" +" This will set a persistent ignore on or the hostmask\n" +" currently associated with . is an optional argument\n" +" specifying when (in \"seconds from now\") the ignore will expire; if\n" +" it isn't given, the ignore will never automatically expire.\n" +" " +msgstr "" + +#: plugin.py:322 +#, docstring +msgid "" +"\n" +"\n" +" This will remove the persistent ignore on or the\n" +" hostmask currently associated with .\n" +" " +msgstr "" + +#: plugin.py:331 +msgid "%s wasn't in the ignores database." +msgstr "" + +#: plugin.py:336 +#, docstring +msgid "" +"takes no arguments\n" +"\n" +" Lists the hostmasks that the bot is ignoring.\n" +" " +msgstr "" + +#: plugin.py:344 +msgid "I'm not currently globally ignoring anyone." +msgstr "" + diff --git a/plugins/Admin/plugin.py b/plugins/Admin/plugin.py index 149d08235..205856a2d 100644 --- a/plugins/Admin/plugin.py +++ b/plugins/Admin/plugin.py @@ -1,5 +1,6 @@ ### # Copyright (c) 2002-2005, Jeremiah Fincher +# Copyright (c) 2010, Valentin Lorentz # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -38,6 +39,8 @@ import supybot.ircmsgs as ircmsgs import supybot.ircutils as ircutils import supybot.schedule as schedule import supybot.callbacks as callbacks +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Admin') class Admin(callbacks.Plugin): def __init__(self, irc): @@ -46,6 +49,7 @@ class Admin(callbacks.Plugin): self.joins = {} self.pendingNickChanges = {} + @internationalizeDocstring def do437(self, irc, msg): """Nick/channel temporarily unavailable.""" target = msg.args[0] @@ -65,7 +69,7 @@ class Admin(callbacks.Plugin): try: channel = msg.args[1] (irc, msg) = self.joins.pop(channel) - irc.error('Cannot join %s, it\'s full.' % channel) + irc.error(_('Cannot join %s, it\'s full.') % channel) except KeyError: self.log.debug('Got 471 without Admin.join being called.') @@ -73,7 +77,7 @@ class Admin(callbacks.Plugin): try: channel = msg.args[1] (irc, msg) = self.joins.pop(channel) - irc.error('Cannot join %s, I was not invited.' % channel) + irc.error(_('Cannot join %s, I was not invited.') % channel) except KeyError: self.log.debug('Got 473 without Admin.join being called.') @@ -81,7 +85,7 @@ class Admin(callbacks.Plugin): try: channel = msg.args[1] (irc, msg) = self.joins.pop(channel) - irc.error('Cannot join %s, it\'s banned me.' % channel) + irc.error(_('Cannot join %s, it\'s banned me.') % channel) except KeyError: self.log.debug('Got 474 without Admin.join being called.') @@ -89,7 +93,7 @@ class Admin(callbacks.Plugin): try: channel = msg.args[1] (irc, msg) = self.joins.pop(channel) - irc.error('Cannot join %s, my keyword was wrong.' % channel) + irc.error(_('Cannot join %s, my keyword was wrong.') % channel) except KeyError: self.log.debug('Got 475 without Admin.join being called.') @@ -97,8 +101,8 @@ class Admin(callbacks.Plugin): try: channel = msg.args[1] (irc, msg) = self.joins.pop(channel) - irc.error('Cannot join %s, I\'m not identified with the NickServ.' - % channel) + irc.error(_('Cannot join %s, I\'m not identified with the ' + 'NickServ.') % channel) except KeyError: self.log.debug('Got 515 without Admin.join being called.') @@ -125,6 +129,7 @@ class Admin(callbacks.Plugin): 'the user lacked the "admin" capability.', channel, msg.prefix) + @internationalizeDocstring def join(self, irc, msg, args, channel, key): """ [] @@ -139,13 +144,14 @@ class Admin(callbacks.Plugin): networkGroup.channels.key.get(channel).setValue(key) maxchannels = irc.state.supported.get('maxchannels', sys.maxint) if len(irc.state.channels) + 1 > maxchannels: - irc.error('I\'m already too close to maximum number of ' - 'channels for this network.', Raise=True) + irc.error(_('I\'m already too close to maximum number of ' + 'channels for this network.'), Raise=True) irc.queueMsg(networkGroup.channels.join(channel)) irc.noReply() self.joins[channel] = (irc, msg) join = wrap(join, ['validChannel', additional('something')]) + @internationalizeDocstring def channels(self, irc, msg, args): """takes no arguments @@ -157,34 +163,34 @@ class Admin(callbacks.Plugin): utils.sortBy(ircutils.toLower, L) irc.reply(format('%L', L)) else: - irc.reply('I\'m not currently in any channels.') + irc.reply(_('I\'m not currently in any channels.')) channels = wrap(channels, ['private']) def do484(self, irc, msg): irc = self.pendingNickChanges.get(irc, None) if irc is not None: - irc.error('My connection is restricted, I can\'t change nicks.') + irc.error(_('My connection is restricted, I can\'t change nicks.')) else: self.log.debug('Got 484 without Admin.nick being called.') def do433(self, irc, msg): irc = self.pendingNickChanges.get(irc, None) if irc is not None: - irc.error('Someone else is already using that nick.') + irc.error(_('Someone else is already using that nick.')) else: self.log.debug('Got 433 without Admin.nick being called.') def do435(self, irc, msg): irc = self.pendingNickChanges.get(irc, None) if irc is not None: - irc.error('That nick is currently banned.') + irc.error(_('That nick is currently banned.')) else: self.log.debug('Got 435 without Admin.nick being called.') def do438(self, irc, msg): irc = self.pendingNickChanges.get(irc, None) if irc is not None: - irc.error(format('I can\'t change nicks, the server said %q.', + irc.error(format(_('I can\'t change nicks, the server said %q.'), msg.args[2]), private=True) else: self.log.debug('Got 438 without Admin.nick being called.') @@ -196,6 +202,7 @@ class Admin(callbacks.Plugin): except KeyError: self.log.debug('Got NICK without Admin.nick being called.') + @internationalizeDocstring def nick(self, irc, msg, args, nick): """[] @@ -210,6 +217,7 @@ class Admin(callbacks.Plugin): irc.reply(irc.nick) nick = wrap(nick, [additional('nick')]) + @internationalizeDocstring def part(self, irc, msg, args, channel, reason): """[] [] @@ -229,7 +237,7 @@ class Admin(callbacks.Plugin): except KeyError: pass if channel not in irc.state.channels: - irc.error('I\'m not in %s.' % channel, Raise=True) + irc.error(_('I\'m not in %s.') % channel, Raise=True) irc.queueMsg(ircmsgs.part(channel, reason or msg.nick)) if msg.nick in irc.state.channels[channel].users: irc.noReply() @@ -239,6 +247,7 @@ class Admin(callbacks.Plugin): class capability(callbacks.Commands): + @internationalizeDocstring def add(self, irc, msg, args, user, capability): """ @@ -260,10 +269,10 @@ class Admin(callbacks.Plugin): # will depend on supybot.capabilities and its child default) but # generally means they can't mess with channel capabilities. if ircutils.strEqual(capability, 'owner'): - irc.error('The "owner" capability can\'t be added in the bot.' - ' Use the supybot-adduser program (or edit the ' + irc.error(_('The "owner" capability can\'t be added in the' + 'bot. Use the supybot-adduser program (or edit the ' 'users.conf file yourself) to add an owner ' - 'capability.') + 'capability.')) return if ircdb.isAntiCapability(capability) or \ ircdb.checkCapability(msg.prefix, capability): @@ -271,9 +280,10 @@ class Admin(callbacks.Plugin): ircdb.users.setUser(user) irc.replySuccess() else: - irc.error('You can\'t add capabilities you don\'t have.') + irc.error(_('You can\'t add capabilities you don\'t have.')) add = wrap(add, ['otherUser', 'lowered']) + @internationalizeDocstring def remove(self, irc, msg, args, user, capability): """ @@ -287,14 +297,15 @@ class Admin(callbacks.Plugin): ircdb.users.setUser(user) irc.replySuccess() except KeyError: - irc.error('That user doesn\'t have that capability.') + irc.error(_('That user doesn\'t have that capability.')) else: - s = 'You can\'t remove capabilities you don\'t have.' + s = _('You can\'t remove capabilities you don\'t have.') irc.error(s) remove = wrap(remove, ['otherUser','lowered']) class ignore(callbacks.Commands): + @internationalizeDocstring def add(self, irc, msg, args, hostmask, expires): """ [] @@ -307,6 +318,7 @@ class Admin(callbacks.Plugin): irc.replySuccess() add = wrap(add, ['hostmask', additional('expiry', 0)]) + @internationalizeDocstring def remove(self, irc, msg, args, hostmask): """ @@ -317,9 +329,10 @@ class Admin(callbacks.Plugin): ircdb.ignores.remove(hostmask) irc.replySuccess() except KeyError: - irc.error('%s wasn\'t in the ignores database.' % hostmask) + irc.error(_('%s wasn\'t in the ignores database.') % hostmask) remove = wrap(remove, ['hostmask']) + @internationalizeDocstring def list(self, irc, msg, args): """takes no arguments @@ -329,7 +342,7 @@ class Admin(callbacks.Plugin): if ircdb.ignores.hostmasks: irc.reply(format('%L', (map(repr,ircdb.ignores.hostmasks)))) else: - irc.reply('I\'m not currently globally ignoring anyone.') + irc.reply(_('I\'m not currently globally ignoring anyone.')) list = wrap(list) From 24ac2c072929fb1a7e4a5ef963bb5648e7f7b4e9 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sat, 16 Oct 2010 09:53:21 +0200 Subject: [PATCH 016/136] Edit .gitignore in order to ignore backup/ repertory (created by supybot-test) --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 450fc4869..b41eb555e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +backup build test-data test-conf From 83c066eeabfd453d4145fa9903abbe2c935d1608 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sat, 16 Oct 2010 10:17:51 +0200 Subject: [PATCH 017/136] Internationalize Channel --- plugins/Channel/config.py | 6 +- plugins/Channel/messages.pot | 621 +++++++++++++++++++++++++++++++++++ plugins/Channel/plugin.py | 159 +++++---- 3 files changed, 724 insertions(+), 62 deletions(-) create mode 100644 plugins/Channel/messages.pot diff --git a/plugins/Channel/config.py b/plugins/Channel/config.py index 023073dde..2e6217435 100644 --- a/plugins/Channel/config.py +++ b/plugins/Channel/config.py @@ -32,6 +32,8 @@ import supybot.conf as conf import supybot.utils as utils import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Channel') def configure(advanced): # This will be called by supybot to configure this module. advanced is @@ -43,8 +45,8 @@ def configure(advanced): Channel = conf.registerPlugin('Channel') conf.registerChannelValue(Channel, 'alwaysRejoin', - registry.Boolean(True, """Determines whether the bot will always try to - rejoin a channel whenever it's kicked from the channel.""")) + registry.Boolean(True, _("""Determines whether the bot will always try to + rejoin a channel whenever it's kicked from the channel."""))) # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/Channel/messages.pot b/plugins/Channel/messages.pot new file mode 100644 index 000000000..4e73c5e42 --- /dev/null +++ b/plugins/Channel/messages.pot @@ -0,0 +1,621 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-16 10:17+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: config.py:48 +msgid "" +"Determines whether the bot will always try to\n" +" rejoin a channel whenever it's kicked from the channel." +msgstr "" + +#: plugin.py:69 +#, docstring +msgid "" +"[] [ ...]\n" +"\n" +" Sets the mode in to , sending the arguments given.\n" +" is only necessary if the message isn't sent in the channel\n" +" itself.\n" +" " +msgstr "" + +#: plugin.py:76 +msgid "change the mode" +msgstr "" + +#: plugin.py:80 +#, docstring +msgid "" +"[] []\n" +"\n" +" Sets the channel limit to . If is 0, or isn't given,\n" +" removes the channel limit. is only necessary if the message\n" +" isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:90 +msgid "change the limit" +msgstr "" + +#: plugin.py:95 +#, docstring +msgid "" +"[]\n" +"\n" +" Sets +m on , making it so only ops and voiced users can\n" +" send messages to the channel. is only necessary if the\n" +" message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:102 +msgid "moderate the channel" +msgstr "" + +#: plugin.py:106 +#, docstring +msgid "" +"[]\n" +"\n" +" Sets -m on , making it so everyone can\n" +" send messages to the channel. is only necessary if the\n" +" message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:114 +msgid "unmoderate the channel" +msgstr "" + +#: plugin.py:118 +#, docstring +msgid "" +"[] []\n" +"\n" +" Sets the keyword in to . If is not given, removes\n" +" the keyword requirement to join . is only necessary\n" +" if the message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:130 +msgid "change the keyword" +msgstr "" + +#: plugin.py:135 +#, docstring +msgid "" +"[] [ ...]\n" +"\n" +" If you have the #channel,op capability, this will give all the s\n" +" you provide ops. If you don't provide any s, this will op you.\n" +" is only necessary if the message isn't sent in the channel\n" +" itself.\n" +" " +msgstr "" + +#: plugin.py:147 +msgid "op someone" +msgstr "" + +#: plugin.py:151 +#, docstring +msgid "" +"[] [ ...]\n" +"\n" +" If you have the #channel,halfop capability, this will give all the\n" +" s you provide halfops. If you don't provide any s, this\n" +" will give you halfops. is only necessary if the message isn't\n" +" sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:163 +msgid "halfop someone" +msgstr "" + +#: plugin.py:168 +#, docstring +msgid "" +"[] [ ...]\n" +"\n" +" If you have the #channel,voice capability, this will voice all the\n" +" s you provide. If you don't provide any s, this will\n" +" voice you. is only necessary if the message isn't sent in the\n" +" channel itself.\n" +" " +msgstr "" + +#: plugin.py:190 +msgid "voice someone" +msgstr "" + +#: plugin.py:195 +#, docstring +msgid "" +"[] [ ...]\n" +"\n" +" If you have the #channel,op capability, this will remove operator\n" +" privileges from all the nicks given. If no nicks are given, removes\n" +" operator privileges from the person sending the message.\n" +" " +msgstr "" + +#: plugin.py:202 +msgid "I cowardly refuse to deop myself. If you really want me deopped, tell me to op you and then deop me yourself." +msgstr "" + +#: plugin.py:210 +msgid "deop someone" +msgstr "" + +#: plugin.py:215 +#, docstring +msgid "" +"[] [ ...]\n" +"\n" +" If you have the #channel,op capability, this will remove half-operator\n" +" privileges from all the nicks given. If no nicks are given, removes\n" +" half-operator privileges from the person sending the message.\n" +" " +msgstr "" + +#: plugin.py:222 +msgid "I cowardly refuse to dehalfop myself. If you really want me dehalfopped, tell me to op you and then dehalfop me yourself." +msgstr "" + +#: plugin.py:230 +msgid "dehalfop someone" +msgstr "" + +#: plugin.py:235 +#, docstring +msgid "" +"[] [ ...]\n" +"\n" +" If you have the #channel,op capability, this will remove voice from all\n" +" the nicks given. If no nicks are given, removes voice from the person\n" +" sending the message.\n" +" " +msgstr "" + +#: plugin.py:242 +msgid "I cowardly refuse to devoice myself. If you really want me devoiced, tell me to op you and then devoice me yourself." +msgstr "" + +#: plugin.py:255 +#, docstring +msgid "" +"[]\n" +"\n" +" If you have the #channel,op capability, this will cause the bot to\n" +" \"cycle\", or PART and then JOIN the channel. is only necessary\n" +" if the message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:268 +#, docstring +msgid "" +"[] [, , ...] []\n" +"\n" +" Kicks (s) from for . If isn't given,\n" +" uses the nick of the person making the command as the reason.\n" +" is only necessary if the message isn't sent in the channel\n" +" itself.\n" +" " +msgstr "" + +#: plugin.py:276 +msgid "I cowardly refuse to kick myself." +msgstr "" + +#: plugin.py:281 +msgid "The reason you gave is longer than the allowed length for a KICK reason on this server." +msgstr "" + +#: plugin.py:286 +msgid "kick someone" +msgstr "" + +#: plugin.py:292 +#, docstring +msgid "" +"[] [--{exact,nick,user,host}] [] []\n" +"\n" +" If you have the #channel,op capability, this will kickban for\n" +" as many seconds as you specify, or else (if you specify 0 seconds or\n" +" don't specify a number of seconds) it will ban the person indefinitely.\n" +" --exact bans only the exact hostmask; --nick bans just the nick;\n" +" --user bans just the user, and --host bans just the host. You can\n" +" combine these options as you choose. is a reason to give for\n" +" the kick.\n" +" is only necessary if the message isn't sent in the channel\n" +" itself.\n" +" " +msgstr "" + +#: plugin.py:311 +msgid "I cowardly refuse to kickban myself." +msgstr "" + +#: plugin.py:318 +msgid "I haven't seen %s." +msgstr "" + +#: plugin.py:326 +msgid "I cowardly refuse to ban myself." +msgstr "" + +#: plugin.py:352 +msgid "%s has %s too, you can't ban him/her/it." +msgstr "" + +#: plugin.py:364 +msgid "kick or ban someone" +msgstr "" + +#: plugin.py:371 +#, docstring +msgid "" +"[] []\n" +"\n" +" Unbans on . If is not given, unbans\n" +" any hostmask currently banned on that matches your current\n" +" hostmask. Especially useful for unbanning yourself when you get\n" +" unexpectedly (or accidentally) banned from the channel. is\n" +" only necessary if the message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:388 +msgid "All bans on %s matching %s have been removed." +msgstr "" + +#: plugin.py:392 +msgid "No bans matching %s were found on %s." +msgstr "" + +#: plugin.py:395 +msgid "unban someone" +msgstr "" + +#: plugin.py:400 +#, docstring +msgid "" +"[] \n" +"\n" +" If you have the #channel,op capability, this will invite \n" +" to join . is only necessary if the message isn't\n" +" sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:409 +msgid "haveOp" +msgstr "" + +#: plugin.py:409 +msgid "invite someone" +msgstr "" + +#: plugin.py:428 +msgid "%s is already in %s." +msgstr "" + +#: plugin.py:435 +msgid "There is no %s on this network." +msgstr "" + +#: plugin.py:447 +#, docstring +msgid "" +"[]\n" +"\n" +" If you have the #channel,op capability, this will \"lobotomize\" the\n" +" bot, making it silent and unanswering to all requests made in the\n" +" channel. is only necessary if the message isn't sent in\n" +" the channel itself.\n" +" " +msgstr "" + +#: plugin.py:462 +#, docstring +msgid "" +"[]\n" +"\n" +" If you have the #channel,op capability, this will unlobotomize the\n" +" bot, making it respond to requests made in the channel again.\n" +" is only necessary if the message isn't sent in the channel\n" +" itself.\n" +" " +msgstr "" + +#: plugin.py:477 +#, docstring +msgid "" +"takes no arguments\n" +"\n" +" Returns the channels in which this bot is lobotomized.\n" +" " +msgstr "" + +#: plugin.py:492 +msgid "I'm currently lobotomized in %L." +msgstr "" + +#: plugin.py:495 +msgid "I'm not currently lobotomized in any channels that you're in." +msgstr "" + +#: plugin.py:502 +#, docstring +msgid "" +"[] []\n" +"\n" +" If you have the #channel,op capability, this will effect a\n" +" persistent ban from interacting with the bot on the given\n" +" (or the current hostmask associated with . Other\n" +" plugins may enforce this ban by actually banning users with\n" +" matching hostmasks when they join. is an optional\n" +" argument specifying when (in \"seconds from now\") the ban should\n" +" expire; if none is given, the ban will never automatically expire.\n" +" is only necessary if the message isn't sent in the\n" +" channel itself.\n" +" " +msgstr "" + +#: plugin.py:522 +#, docstring +msgid "" +"[] \n" +"\n" +" If you have the #channel,op capability, this will remove the\n" +" persistent ban on . is only necessary if the\n" +" message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:534 +msgid "There are no persistent bans for that hostmask." +msgstr "" + +#: plugin.py:539 +#, docstring +msgid "" +"[]\n" +"\n" +" If you have the #channel,op capability, this will show you the\n" +" current persistent bans on #channel.\n" +" " +msgstr "" + +#: plugin.py:549 +msgid "%q (expires %t)" +msgstr "" + +#: plugin.py:552 +msgid "%q (never expires)" +msgstr "" + +#: plugin.py:556 +msgid "There are no persistent bans on %s." +msgstr "" + +#: plugin.py:563 +#, docstring +msgid "" +"[] []\n" +"\n" +" If you have the #channel,op capability, this will set a persistent\n" +" ignore on or the hostmask currently\n" +" associated with . is an optional argument\n" +" specifying when (in \"seconds from now\") the ignore will expire; if\n" +" it isn't given, the ignore will never automatically expire.\n" +" is only necessary if the message isn't sent in the\n" +" channel itself.\n" +" " +msgstr "" + +#: plugin.py:581 +#, docstring +msgid "" +"[] \n" +"\n" +" If you have the #channel,op capability, this will remove the\n" +" persistent ignore on in the channel. is only\n" +" necessary if the message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:593 +msgid "There are no ignores for that hostmask." +msgstr "" + +#: plugin.py:598 +#, docstring +msgid "" +"[]\n" +"\n" +" Lists the hostmasks that the bot is ignoring on the given channel.\n" +" is only necessary if the message isn't sent in the\n" +" channel itself.\n" +" " +msgstr "" + +#: plugin.py:607 +msgid "I'm not currently ignoring any hostmasks in %q" +msgstr "" + +#: plugin.py:618 +#, docstring +msgid "" +"[] [ ...]\n" +"\n" +" If you have the #channel,op capability, this will give the user\n" +" (or the user to whom maps)\n" +" the capability in the channel. is only\n" +" necessary if the message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:634 +#, docstring +msgid "" +"[] [ ...]\n" +"\n" +" If you have the #channel,op capability, this will take from the\n" +" user currently identified as (or the user to whom \n" +" maps) the capability in the channel. is only\n" +" necessary if the message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:653 +msgid "That user didn't have the %L %s." +msgstr "" + +#: plugin.py:662 +#, docstring +msgid "" +"[] {True|False}\n" +"\n" +" If you have the #channel,op capability, this will set the default\n" +" response to non-power-related (that is, not {op, halfop, voice}\n" +" capabilities to be the value you give. is only necessary\n" +" if the message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:680 +#, docstring +msgid "" +"[] [ ...]\n" +"\n" +" If you have the #channel,op capability, this will add the channel\n" +" capability for all users in the channel. is\n" +" only necessary if the message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:695 +#, docstring +msgid "" +"[] [ ...]\n" +"\n" +" If you have the #channel,op capability, this will unset the channel\n" +" capability so each user's specific capability or the\n" +" channel default capability will take precedence. is only\n" +" necessary if the message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:711 +msgid "capability" +msgstr "" + +#: plugin.py:714 +msgid "I do not know about the %L %s." +msgstr "" + +#: plugin.py:721 +#, docstring +msgid "" +"[]\n" +"\n" +" Returns the capabilities present on the . is\n" +" only necessary if the message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:733 +#, docstring +msgid "" +"[] [] []\n" +"\n" +" If you have the #channel,op capability, this will disable the \n" +" in . If is provided, will be disabled only\n" +" for that plugin. If only is provided, all commands in the\n" +" given plugin will be disabled. is only necessary if the\n" +" message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:749 plugin.py:788 +msgid "The %s plugin does not have a command called %s." +msgstr "" + +#: plugin.py:756 plugin.py:795 +msgid "No plugin or command named %s could be found." +msgstr "" + +#: plugin.py:772 +#, docstring +msgid "" +"[] [] []\n" +"\n" +" If you have the #channel,op capability, this will enable the \n" +" in if it has been disabled. If is provided,\n" +" will be enabled only for that plugin. If only is\n" +" provided, all commands in the given plugin will be enabled. \n" +" is only necessary if the message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:809 +msgid "%s was not disabled." +msgstr "" + +#: plugin.py:818 +#, docstring +msgid "" +"[]\n" +"\n" +" Returns the nicks in . is only necessary if the\n" +" message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:829 +msgid "You don't have access to that information." +msgstr "" + +#: plugin.py:837 +#, docstring +msgid "" +"Internal message for notifying all the #channel,ops in a channel of\n" +" a given situation." +msgstr "" + +#: plugin.py:840 +msgid "Alert to all %s ops: %s" +msgstr "" + +#: plugin.py:842 +msgid " (from %s)" +msgstr "" + +#: plugin.py:850 +#, docstring +msgid "" +"[] \n" +"\n" +" Sends to all the users in who have the ,op\n" +" capability.\n" +" " +msgstr "" + diff --git a/plugins/Channel/plugin.py b/plugins/Channel/plugin.py index de12e91cc..9e65004f6 100644 --- a/plugins/Channel/plugin.py +++ b/plugins/Channel/plugin.py @@ -38,6 +38,8 @@ import supybot.ircmsgs as ircmsgs import supybot.schedule as schedule import supybot.ircutils as ircutils import supybot.callbacks as callbacks +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Channel') class Channel(callbacks.Plugin): def __init__(self, irc): @@ -62,6 +64,7 @@ class Channel(callbacks.Plugin): irc.queueMsg(f(nicks[i:i + numModes])) irc.noReply() + @internationalizeDocstring def mode(self, irc, msg, args, channel, modes): """[] [ ...] @@ -70,8 +73,9 @@ class Channel(callbacks.Plugin): itself. """ self._sendMsg(irc, ircmsgs.mode(channel, modes)) - mode = wrap(mode, ['op', ('haveOp', 'change the mode'), many('something')]) + mode = wrap(mode, ['op', ('haveOp', _('change the mode')), many('something')]) + @internationalizeDocstring def limit(self, irc, msg, args, channel, limit): """[] [] @@ -83,9 +87,10 @@ class Channel(callbacks.Plugin): self._sendMsg(irc, ircmsgs.mode(channel, ['+l', limit])) else: self._sendMsg(irc, ircmsgs.mode(channel, ['-l'])) - limit = wrap(limit, ['op', ('haveOp', 'change the limit'), + limit = wrap(limit, ['op', ('haveOp', _('change the limit')), additional('nonNegativeInt', 0)]) + @internationalizeDocstring def moderate(self, irc, msg, args, channel): """[] @@ -94,8 +99,9 @@ class Channel(callbacks.Plugin): message isn't sent in the channel itself. """ self._sendMsg(irc, ircmsgs.mode(channel, ['+m'])) - moderate = wrap(moderate, ['op', ('haveOp', 'moderate the channel')]) + moderate = wrap(moderate, ['op', ('haveOp', _('moderate the channel'))]) + @internationalizeDocstring def unmoderate(self, irc, msg, args, channel): """[] @@ -104,8 +110,10 @@ class Channel(callbacks.Plugin): message isn't sent in the channel itself. """ self._sendMsg(irc, ircmsgs.mode(channel, ['-m'])) - unmoderate = wrap(unmoderate, ['op', ('haveOp', 'unmoderate the channel')]) + unmoderate = wrap(unmoderate, ['op', ('haveOp', + _('unmoderate the channel'))]) + @internationalizeDocstring def key(self, irc, msg, args, channel, key): """[] [] @@ -119,9 +127,10 @@ class Channel(callbacks.Plugin): self._sendMsg(irc, ircmsgs.mode(channel, ['+k', key])) else: self._sendMsg(irc, ircmsgs.mode(channel, ['-k'])) - key = wrap(key, ['op', ('haveOp', 'change the keyword'), + key = wrap(key, ['op', ('haveOp', _('change the keyword')), additional('somethingWithoutSpaces', '')]) + @internationalizeDocstring def op(self, irc, msg, args, channel, nicks): """[] [ ...] @@ -135,8 +144,9 @@ class Channel(callbacks.Plugin): def f(L): return ircmsgs.ops(channel, L) self._sendMsgs(irc, nicks, f) - op = wrap(op, ['op', ('haveOp', 'op someone'), any('nickInChannel')]) + op = wrap(op, ['op', ('haveOp', _('op someone')), any('nickInChannel')]) + @internationalizeDocstring def halfop(self, irc, msg, args, channel, nicks): """[] [ ...] @@ -150,9 +160,10 @@ class Channel(callbacks.Plugin): def f(L): return ircmsgs.halfops(channel, L) self._sendMsgs(irc, nicks, f) - halfop = wrap(halfop, ['halfop', ('haveOp', 'halfop someone'), + halfop = wrap(halfop, ['halfop', ('haveOp', _('halfop someone')), any('nickInChannel')]) + @internationalizeDocstring def voice(self, irc, msg, args, channel, nicks): """[] [ ...] @@ -176,9 +187,10 @@ class Channel(callbacks.Plugin): self._sendMsgs(irc, nicks, f) else: irc.errorNoCapability(capability) - voice = wrap(voice, ['channel', ('haveOp', 'voice someone'), + voice = wrap(voice, ['channel', ('haveOp', _('voice someone')), any('nickInChannel')]) + @internationalizeDocstring def deop(self, irc, msg, args, channel, nicks): """[] [ ...] @@ -187,17 +199,18 @@ class Channel(callbacks.Plugin): operator privileges from the person sending the message. """ if irc.nick in nicks: - irc.error('I cowardly refuse to deop myself. If you really want ' - 'me deopped, tell me to op you and then deop me ' - 'yourself.', Raise=True) + irc.error(_('I cowardly refuse to deop myself. If you really ' + 'want me deopped, tell me to op you and then deop me ' + 'yourself.'), Raise=True) if not nicks: nicks = [msg.nick] def f(L): return ircmsgs.deops(channel, L) self._sendMsgs(irc, nicks, f) - deop = wrap(deop, ['op', ('haveOp', 'deop someone'), + deop = wrap(deop, ['op', ('haveOp', _('deop someone')), any('nickInChannel')]) + @internationalizeDocstring def dehalfop(self, irc, msg, args, channel, nicks): """[] [ ...] @@ -206,17 +219,18 @@ class Channel(callbacks.Plugin): half-operator privileges from the person sending the message. """ if irc.nick in nicks: - irc.error('I cowardly refuse to dehalfop myself. If you really ' + irc.error(_('I cowardly refuse to dehalfop myself. If you really ' 'want me dehalfopped, tell me to op you and then ' - 'dehalfop me yourself.', Raise=True) + 'dehalfop me yourself.'), Raise=True) if not nicks: nicks = [msg.nick] def f(L): return ircmsgs.dehalfops(channel, L) self._sendMsgs(irc, nicks, f) - dehalfop = wrap(dehalfop, ['halfop', ('haveOp', 'dehalfop someone'), + dehalfop = wrap(dehalfop, ['halfop', ('haveOp', _('dehalfop someone')), any('nickInChannel')]) + @internationalizeDocstring def devoice(self, irc, msg, args, channel, nicks): """[] [ ...] @@ -225,9 +239,9 @@ class Channel(callbacks.Plugin): sending the message. """ if irc.nick in nicks: - irc.error('I cowardly refuse to devoice myself. If you really ' + irc.error(_('I cowardly refuse to devoice myself. If you really ' 'want me devoiced, tell me to op you and then devoice ' - 'me yourself.', Raise=True) + 'me yourself.'), Raise=True) if not nicks: nicks = [msg.nick] def f(L): @@ -236,6 +250,7 @@ class Channel(callbacks.Plugin): devoice = wrap(devoice, ['voice', ('haveOp', 'devoice someone'), any('nickInChannel')]) + @internationalizeDocstring def cycle(self, irc, msg, args, channel): """[] @@ -248,6 +263,7 @@ class Channel(callbacks.Plugin): self._sendMsg(irc, networkGroup.channels.join(channel)) cycle = wrap(cycle, ['op']) + @internationalizeDocstring def kick(self, irc, msg, args, channel, nicks, reason): """[] [, , ...] [] @@ -257,19 +273,20 @@ class Channel(callbacks.Plugin): itself. """ if utils.iter.any(lambda n: ircutils.strEqual(n, irc.nick), nicks): - irc.error('I cowardly refuse to kick myself.', Raise=True) + irc.error(_('I cowardly refuse to kick myself.'), Raise=True) if not reason: reason = msg.nick kicklen = irc.state.supported.get('kicklen', sys.maxint) if len(reason) > kicklen: - irc.error('The reason you gave is longer than the allowed ' - 'length for a KICK reason on this server.', + irc.error(_('The reason you gave is longer than the allowed ' + 'length for a KICK reason on this server.'), Raise=True) for nick in nicks: self._sendMsg(irc, ircmsgs.kick(channel, nick, reason)) - kick = wrap(kick, ['op', ('haveOp', 'kick someone'), + kick = wrap(kick, ['op', ('haveOp', _('kick someone')), commalist('nickInChannel'), additional('text')]) + @internationalizeDocstring def kban(self, irc, msg, args, channel, optlist, bannedNick, expiry, reason): """[] [--{exact,nick,user,host}] [] [] @@ -291,14 +308,14 @@ class Channel(callbacks.Plugin): raise callbacks.ArgumentError elif bannedNick == irc.nick: self.log.warning('%q tried to make me kban myself.', msg.prefix) - irc.error('I cowardly refuse to kickban myself.') + irc.error(_('I cowardly refuse to kickban myself.')) return if not reason: reason = msg.nick try: bannedHostmask = irc.state.nickToHostmask(bannedNick) except KeyError: - irc.error(format('I haven\'t seen %s.', bannedNick), Raise=True) + irc.error(format(_('I haven\'t seen %s.'), bannedNick), Raise=True) capability = ircdb.makeChannelCapability(channel, 'op') banmaskstyle = conf.supybot.protocols.irc.banmask banmask = banmaskstyle.makeBanmask(bannedHostmask, [o[0] for o in optlist]) @@ -306,7 +323,7 @@ class Channel(callbacks.Plugin): if ircutils.hostmaskPatternEqual(banmask, irc.prefix): if ircutils.hostmaskPatternEqual(bannedHostmask, irc.prefix): self.log.warning('%q tried to make me kban myself.',msg.prefix) - irc.error('I cowardly refuse to ban myself.') + irc.error(_('I cowardly refuse to ban myself.')) return else: self.log.warning('Using exact hostmask since banmask would ' @@ -332,8 +349,8 @@ class Channel(callbacks.Plugin): if ircdb.checkCapability(bannedHostmask, capability): self.log.warning('%s tried to ban %q, but both have %s', msg.prefix, bannedHostmask, capability) - irc.error(format('%s has %s too, you can\'t ban him/her/it.', - bannedNick, capability)) + irc.error(format(_('%s has %s too, you can\'t ban ' + 'him/her/it.'), bannedNick, capability)) else: doBan() else: @@ -344,11 +361,12 @@ class Channel(callbacks.Plugin): kban = wrap(kban, ['op', getopts({'exact':'', 'nick':'', 'user':'', 'host':''}), - ('haveOp', 'kick or ban someone'), + ('haveOp', _('kick or ban someone')), 'nickInChannel', optional('expiry', 0), additional('text')]) + @internationalizeDocstring def unban(self, irc, msg, args, channel, hostmask): """[] [] @@ -367,16 +385,17 @@ class Channel(callbacks.Plugin): bans.append(banmask) if bans: irc.queueMsg(ircmsgs.unbans(channel, bans)) - irc.replySuccess(format('All bans on %s matching %s ' - 'have been removed.', + irc.replySuccess(format(_('All bans on %s matching %s ' + 'have been removed.'), channel, msg.prefix)) else: - irc.error('No bans matching %s were found on %s.' % + irc.error(_('No bans matching %s were found on %s.') % (msg.prefix, channel)) unban = wrap(unban, ['op', - ('haveOp', 'unban someone'), + ('haveOp', _('unban someone')), additional('hostmask')]) + @internationalizeDocstring def invite(self, irc, msg, args, channel, nick): """[] @@ -387,7 +406,7 @@ class Channel(callbacks.Plugin): nick = nick or msg.nick self._sendMsg(irc, ircmsgs.invite(nick, channel)) self.invites[(irc.getRealIrc(), ircutils.toLower(nick))] = irc - invite = wrap(invite, ['op', ('haveOp', 'invite someone'), + invite = wrap(invite, ['op', (_('haveOp'), _('invite someone')), additional('nick')]) def do341(self, irc, msg): @@ -406,14 +425,14 @@ class Channel(callbacks.Plugin): nick = ircutils.toLower(nick) replyIrc = self.invites.pop((irc, nick), None) if replyIrc is not None: - replyIrc.error(format('%s is already in %s.', nick, channel)) + replyIrc.error(format(_('%s is already in %s.'), nick, channel)) def do401(self, irc, msg): nick = msg.args[1] nick = ircutils.toLower(nick) replyIrc = self.invites.pop((irc, nick), None) if replyIrc is not None: - replyIrc.error(format('There is no %s on this network.', nick)) + replyIrc.error(format(_('There is no %s on this network.'), nick)) def do504(self, irc, msg): nick = msg.args[1] @@ -423,6 +442,7 @@ class Channel(callbacks.Plugin): replyIrc.error(format('There is no %s on this server.', nick)) class lobotomy(callbacks.Commands): + @internationalizeDocstring def add(self, irc, msg, args, channel): """[] @@ -437,6 +457,7 @@ class Channel(callbacks.Plugin): irc.replySuccess() add = wrap(add, ['op']) + @internationalizeDocstring def remove(self, irc, msg, args, channel): """[] @@ -451,6 +472,7 @@ class Channel(callbacks.Plugin): irc.replySuccess() remove = wrap(remove, ['op']) + @internationalizeDocstring def list(self, irc, msg, args): """takes no arguments @@ -467,14 +489,15 @@ class Channel(callbacks.Plugin): L.append(channel) if L: L.sort() - s = format('I\'m currently lobotomized in %L.', L) + s = format(_('I\'m currently lobotomized in %L.'), L) irc.reply(s) else: - irc.reply('I\'m not currently lobotomized in any channels ' - 'that you\'re in.') + irc.reply(_('I\'m not currently lobotomized in any channels ' + 'that you\'re in.')) list = wrap(list) class ban(callbacks.Commands): + @internationalizeDocstring def add(self, irc, msg, args, channel, banmask, expires): """[] [] @@ -494,6 +517,7 @@ class Channel(callbacks.Plugin): irc.replySuccess() add = wrap(add, ['op', 'banmask', additional('expiry', 0)]) + @internationalizeDocstring def remove(self, irc, msg, args, channel, banmask): """[] @@ -507,9 +531,10 @@ class Channel(callbacks.Plugin): ircdb.channels.setChannel(channel, c) irc.replySuccess() except KeyError: - irc.error('There are no persistent bans for that hostmask.') + irc.error(_('There are no persistent bans for that hostmask.')) remove = wrap(remove, ['op', 'hostmask']) + @internationalizeDocstring def list(self, irc, msg, args, channel): """[] @@ -521,18 +546,19 @@ class Channel(callbacks.Plugin): bans = [] for ban in c.bans: if c.bans[ban]: - bans.append(format('%q (expires %t)', + bans.append(format(_('%q (expires %t)'), ban, c.bans[ban])) else: - bans.append(format('%q (never expires)', + bans.append(format(_('%q (never expires)'), ban, c.bans[ban])) irc.reply(format('%L', bans)) else: - irc.reply(format('There are no persistent bans on %s.', + irc.reply(format(_('There are no persistent bans on %s.'), channel)) list = wrap(list, ['op']) class ignore(callbacks.Commands): + @internationalizeDocstring def add(self, irc, msg, args, channel, banmask, expires): """[] [] @@ -550,6 +576,7 @@ class Channel(callbacks.Plugin): irc.replySuccess() add = wrap(add, ['op', 'banmask', additional('expiry', 0)]) + @internationalizeDocstring def remove(self, irc, msg, args, channel, banmask): """[] @@ -563,9 +590,10 @@ class Channel(callbacks.Plugin): ircdb.channels.setChannel(channel, c) irc.replySuccess() except KeyError: - irc.error('There are no ignores for that hostmask.') + irc.error(_('There are no ignores for that hostmask.')) remove = wrap(remove, ['op', 'hostmask']) + @internationalizeDocstring def list(self, irc, msg, args, channel): """[] @@ -576,8 +604,8 @@ class Channel(callbacks.Plugin): # XXX Add the expirations. c = ircdb.channels.getChannel(channel) if len(c.ignores) == 0: - s = format('I\'m not currently ignoring any hostmasks in %q', - channel) + s = format(_('I\'m not currently ignoring any hostmasks in ' + '%q'), channel) irc.reply(s) else: L = sorted(c.ignores) @@ -585,6 +613,7 @@ class Channel(callbacks.Plugin): list = wrap(list, ['op']) class capability(callbacks.Commands): + @internationalizeDocstring def add(self, irc, msg, args, channel, user, capabilities): """[] [ ...] @@ -600,6 +629,7 @@ class Channel(callbacks.Plugin): irc.replySuccess() add = wrap(add, ['op', 'otherUser', 'capability']) + @internationalizeDocstring def remove(self, irc, msg, args, channel, user, capabilities): """[] [ ...] @@ -620,13 +650,14 @@ class Channel(callbacks.Plugin): s = 'capability' if len(fail) > 1: s = utils.str.pluralize(s) - irc.error(format('That user didn\'t have the %L %s.', fail, s), - Raise=True) + irc.error(format(_('That user didn\'t have the %L %s.'), fail, + s), Raise=True) irc.replySuccess() remove = wrap(remove, ['op', 'otherUser', 'capability']) # XXX This needs to be fix0red to be like Owner.defaultcapability. Or # something else. This is a horrible interface. + @internationalizeDocstring def setdefault(self, irc, msg, args, channel, v): """[] {True|False} @@ -644,6 +675,7 @@ class Channel(callbacks.Plugin): irc.replySuccess() setdefault = wrap(setdefault, ['op', 'boolean']) + @internationalizeDocstring def set(self, irc, msg, args, channel, capabilities): """[] [ ...] @@ -658,6 +690,7 @@ class Channel(callbacks.Plugin): irc.replySuccess() set = wrap(set, ['op', many('capability')]) + @internationalizeDocstring def unset(self, irc, msg, args, channel, capabilities): """[] [ ...] @@ -675,14 +708,15 @@ class Channel(callbacks.Plugin): fail.append(c) ircdb.channels.setChannel(channel, chan) if fail: - s = 'capability' + s = _('capability') if len(fail) > 1: s = utils.str.pluralize(s) - irc.error(format('I do not know about the %L %s.', fail, s), + irc.error(format(_('I do not know about the %L %s.'), fail, s), Raise=True) irc.replySuccess() unset = wrap(unset, ['op', many('capability')]) + @internationalizeDocstring def list(self, irc, msg, args, channel): """[] @@ -694,6 +728,7 @@ class Channel(callbacks.Plugin): irc.reply(' '.join(L)) list = wrap(list, ['channel']) + @internationalizeDocstring def disable(self, irc, msg, args, channel, plugin, command): """[] [] [] @@ -711,15 +746,15 @@ class Channel(callbacks.Plugin): if plugin.isCommand(command): s = '-%s.%s' % (plugin.name(), command) else: - failMsg = format('The %s plugin does not have a command ' - 'called %s.', plugin.name(), command) + failMsg = format(_('The %s plugin does not have a command ' + 'called %s.'), plugin.name(), command) elif command: # findCallbackForCommand if filter(None, irc.findCallbacksForArgs([command])): s = '-%s' % command else: - failMsg = format('No plugin or command named %s could be ' - 'found.', command) + failMsg = format(_('No plugin or command named %s could be ' + 'found.'), command) else: raise callbacks.ArgumentError if failMsg: @@ -732,6 +767,7 @@ class Channel(callbacks.Plugin): optional(('plugin', False)), additional('commandName')]) + @internationalizeDocstring def enable(self, irc, msg, args, channel, plugin, command): """[] [] [] @@ -749,15 +785,15 @@ class Channel(callbacks.Plugin): if plugin.isCommand(command): s = '-%s.%s' % (plugin.name(), command) else: - failMsg = format('The %s plugin does not have a command ' - 'called %s.', plugin.name(), command) + failMsg = format(_('The %s plugin does not have a command ' + 'called %s.'), plugin.name(), command) elif command: # findCallbackForCommand if filter(None, irc.findCallbacksForArgs([command])): s = '-%s' % command else: - failMsg = format('No plugin or command named %s could be ' - 'found.', command) + failMsg = format(_('No plugin or command named %s could be ' + 'found.'), command) else: raise callbacks.ArgumentError if failMsg: @@ -770,13 +806,14 @@ class Channel(callbacks.Plugin): fail.append(s) ircdb.channels.setChannel(channel, chan) if fail: - irc.error(format('%s was not disabled.', s[1:])) + irc.error(format(_('%s was not disabled.'), s[1:])) else: irc.replySuccess() enable = wrap(enable, ['op', optional(('plugin', False)), additional('commandName')]) + @internationalizeDocstring def nicks(self, irc, msg, args, channel): """[] @@ -789,24 +826,26 @@ class Channel(callbacks.Plugin): msg.args[0] != channel and \ (ircutils.isChannel(msg.args[0]) or \ msg.nick not in irc.state.channels[channel].users): - irc.error('You don\'t have access to that information.') + irc.error(_('You don\'t have access to that information.')) L = list(irc.state.channels[channel].users) utils.sortBy(str.lower, L) irc.reply(utils.str.commaAndify(L)) nicks = wrap(nicks, ['inChannel']) + @internationalizeDocstring def alertOps(self, irc, channel, s, frm=None): """Internal message for notifying all the #channel,ops in a channel of a given situation.""" capability = ircdb.makeChannelCapability(channel, 'op') - s = format('Alert to all %s ops: %s', channel, s) + s = format(_('Alert to all %s ops: %s'), channel, s) if frm is not None: - s += format(' (from %s)', frm) + s += format(_(' (from %s)'), frm) for nick in irc.state.channels[channel].users: hostmask = irc.state.nickToHostmask(nick) if ircdb.checkCapability(hostmask, capability): irc.reply(s, to=nick, private=True) + @internationalizeDocstring def alert(self, irc, msg, args, channel, text): """[] From 2a69fd23c416c671613ab8bd0ae2ed3904796a10 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sat, 16 Oct 2010 10:51:50 +0200 Subject: [PATCH 018/136] Little fix to Admin internationalization --- plugins/Admin/locale/fr.po | 202 +++++++++++++++++++++++++++++++++++++ plugins/Admin/messages.pot | 60 +++++------ 2 files changed, 232 insertions(+), 30 deletions(-) create mode 100644 plugins/Admin/locale/fr.po diff --git a/plugins/Admin/locale/fr.po b/plugins/Admin/locale/fr.po new file mode 100644 index 000000000..872ce6191 --- /dev/null +++ b/plugins/Admin/locale/fr.po @@ -0,0 +1,202 @@ +msgid "" +msgstr "" +"Project-Id-Version: Supybot-fr\n" +"POT-Creation-Date: 2010-10-16 10:43+CEST\n" +"PO-Revision-Date: \n" +"Last-Translator: Valentin Lorentz \n" +"Language-Team: ProgVal \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Poedit-Language: French\n" +"X-Poedit-Country: France\n" +"X-Poedit-SourceCharset: Ascii\n" + +#: plugin.py:54 +msgid "Nick/channel temporarily unavailable." +msgstr "Nick/canal temporairement indisponible" + +#: plugin.py:72 +msgid "Cannot join %s, it's full." +msgstr "Ne peut joindre %s, il est plein" + +#: plugin.py:80 +msgid "Cannot join %s, I was not invited." +msgstr "Ne peut joindre %s, pas invité" + +#: plugin.py:88 +msgid "Cannot join %s, it's banned me." +msgstr "Ne peut joindre %s, j'y suis banni." + +#: plugin.py:96 +msgid "Cannot join %s, my keyword was wrong." +msgstr "Ne peut joindre %s, mon mot de passe est mauvais." + +#: plugin.py:104 +msgid "Cannot join %s, I'm not identified with the NickServ." +msgstr "Ne peut joindre %s, je ne suis pas identifié auprès de NickServ." + +#: plugin.py:134 +msgid "" +" []\n" +"\n" +" Tell the bot to join the given channel. If is given, it is used\n" +" when attempting to join the channel.\n" +" " +msgstr "" +" []\n" +"\n" +"Dit au bot de rejoindre le canal donné. Si la est donnée, elle est utilisée pour rejoindre le canal." + +#: plugin.py:147 +msgid "I'm already too close to maximum number of channels for this network." +msgstr "Je suis déjà sur un nombre de canaux trop grand pour ce réseau." + +#: plugin.py:156 +msgid "" +"takes no arguments\n" +"\n" +" Returns the channels the bot is on. Must be given in private, in order\n" +" to protect the secrecy of secret channels.\n" +" " +msgstr "" +"Ne prend pas d'argument \n" +"\n" +"Retourne les canaux sur lesquels le bot est. Doit être en privé, dans le but d'éviter que les canaux secrets ne soient divulgués." + +#: plugin.py:166 +msgid "I'm not currently in any channels." +msgstr "Je ne suis actuellement sur aucun canal." + +#: plugin.py:172 +msgid "My connection is restricted, I can't change nicks." +msgstr "Ma connexion est restreinte, je ne peux changer de nick." + +#: plugin.py:179 +msgid "Someone else is already using that nick." +msgstr "Quelqu'un d'autre utilise déjà ce nick." + +#: plugin.py:186 +msgid "That nick is currently banned." +msgstr "Ce nick est banni." + +#: plugin.py:193 +msgid "I can't change nicks, the server said %q." +msgstr "Je ne peux changer de nick, le serveur a dit %q." + +#: plugin.py:207 +msgid "" +"[]\n" +"\n" +" Changes the bot's nick to . If no nick is given, returns the\n" +" bot's current nick.\n" +" " +msgstr "" +"[]\n" +"\n" +"Change le nick du bot à . Si aucun nick n'est donné, retourne le nick actuel du bot." + +#: plugin.py:222 +msgid "" +"[] []\n" +"\n" +" Tells the bot to part the list of channels you give it. is\n" +" only necessary if you want the bot to part a channel other than the\n" +" current channel. If is specified, use it as the part\n" +" message.\n" +" " +msgstr "" +"[] []\n" +"\n" +"Dit au bot de partir de la liste de canaux que vous avez donnée. n'est nécessaire que si vous voulez que le bot parte d'un autre canal que l'actuel. Si la est spécifiée, elle est utilisée comme message de départ." + +#: plugin.py:240 +msgid "I'm not in %s." +msgstr "Je ne suis pas sur %s." + +#: plugin.py:252 +msgid "" +" \n" +"\n" +" Gives the user specified by (or the user to whom \n" +" currently maps) the specified capability \n" +" " +msgstr "" +" \n" +"\n" +"Donne la à l'utilisateur spécifié par (ou l'utilisateur à qui correspond )." + +#: plugin.py:272 +msgid "The \"owner\" capability can't be added in the bot. Use the supybot-adduser program (or edit the users.conf file yourself) to add an owner capability." +msgstr "La capabilité \"owner\" ne peut être ajoutée via le bot. Utilisez le programme supybot-adduser (ou éditez le fichier users.conf vous-même) pour ajouter la capacité owner." + +#: plugin.py:283 +msgid "You can't add capabilities you don't have." +msgstr "Vous ne pouvez ajouter des permissions que vous n'avez pas." + +#: plugin.py:288 +msgid "" +" \n" +"\n" +" Takes from the user specified by (or the user to whom\n" +" currently maps) the specified capability \n" +" " +msgstr "" +" \n" +"\n" +"Retire la à l'utilisateur spécifié par le (ou celui à qui correspond le )." + +#: plugin.py:300 +msgid "That user doesn't have that capability." +msgstr "Cet utilisateur n'a pas cette permission." + +#: plugin.py:302 +msgid "You can't remove capabilities you don't have." +msgstr "Vous ne pouvez retirer des permissions que vous n'avez pas." + +#: plugin.py:310 +msgid "" +" []\n" +"\n" +" This will set a persistent ignore on or the hostmask\n" +" currently associated with . is an optional argument\n" +" specifying when (in \"seconds from now\") the ignore will expire; if\n" +" it isn't given, the ignore will never automatically expire.\n" +" " +msgstr "" +" []\n" +"\n" +"Ajoute un masque d'ignorance persistant sur le , ou sur le masque d'hôte de . est un argument optionnel spécifiant quand (en \"secondes à partir de maintenant\") l'ignorance expirera ; si elle n'est pas donnée, l'ignorance n'expirera jamais." + +#: plugin.py:323 +msgid "" +"\n" +"\n" +" This will remove the persistent ignore on or the\n" +" hostmask currently associated with .\n" +" " +msgstr "" +"\n" +"\n" +"Ceci retirera le masque d'ignorance persistant sur le , ou sur le masque d'hôte associé au ." + +#: plugin.py:332 +msgid "%s wasn't in the ignores database." +msgstr "%s n'étais pas dans ma base de données d'ignorance." + +#: plugin.py:337 +msgid "" +"takes no arguments\n" +"\n" +" Lists the hostmasks that the bot is ignoring.\n" +" " +msgstr "" +"Ne prend pas d'argument\n" +"\n" +"Liste les masques d'hôte que le bot ignore." + +#: plugin.py:345 +msgid "I'm not currently globally ignoring anyone." +msgstr "Je n'ignore actuellement personne globalement." + diff --git a/plugins/Admin/messages.pot b/plugins/Admin/messages.pot index e5833f721..673d31155 100644 --- a/plugins/Admin/messages.pot +++ b/plugins/Admin/messages.pot @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2010-10-16 09:41+CEST\n" +"POT-Creation-Date: 2010-10-16 10:43+CEST\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -15,32 +15,32 @@ msgstr "" "Generated-By: pygettext.py 1.5\n" -#: plugin.py:53 +#: plugin.py:54 #, docstring msgid "Nick/channel temporarily unavailable." msgstr "" -#: plugin.py:71 +#: plugin.py:72 msgid "Cannot join %s, it's full." msgstr "" -#: plugin.py:79 +#: plugin.py:80 msgid "Cannot join %s, I was not invited." msgstr "" -#: plugin.py:87 +#: plugin.py:88 msgid "Cannot join %s, it's banned me." msgstr "" -#: plugin.py:95 +#: plugin.py:96 msgid "Cannot join %s, my keyword was wrong." msgstr "" -#: plugin.py:103 +#: plugin.py:104 msgid "Cannot join %s, I'm not identified with the NickServ." msgstr "" -#: plugin.py:133 +#: plugin.py:134 #, docstring msgid "" " []\n" @@ -50,11 +50,11 @@ msgid "" " " msgstr "" -#: plugin.py:146 +#: plugin.py:147 msgid "I'm already too close to maximum number of channels for this network." msgstr "" -#: plugin.py:155 +#: plugin.py:156 #, docstring msgid "" "takes no arguments\n" @@ -64,27 +64,27 @@ msgid "" " " msgstr "" -#: plugin.py:165 +#: plugin.py:166 msgid "I'm not currently in any channels." msgstr "" -#: plugin.py:171 +#: plugin.py:172 msgid "My connection is restricted, I can't change nicks." msgstr "" -#: plugin.py:178 +#: plugin.py:179 msgid "Someone else is already using that nick." msgstr "" -#: plugin.py:185 +#: plugin.py:186 msgid "That nick is currently banned." msgstr "" -#: plugin.py:192 +#: plugin.py:193 msgid "I can't change nicks, the server said %q." msgstr "" -#: plugin.py:206 +#: plugin.py:207 #, docstring msgid "" "[]\n" @@ -94,7 +94,7 @@ msgid "" " " msgstr "" -#: plugin.py:221 +#: plugin.py:222 #, docstring msgid "" "[] []\n" @@ -106,11 +106,11 @@ msgid "" " " msgstr "" -#: plugin.py:239 +#: plugin.py:240 msgid "I'm not in %s." msgstr "" -#: plugin.py:251 +#: plugin.py:252 #, docstring msgid "" " \n" @@ -120,15 +120,15 @@ msgid "" " " msgstr "" -#: plugin.py:271 -msgid "The \"owner\" capability can't be added in thebot. Use the supybot-adduser program (or edit the users.conf file yourself) to add an owner capability." +#: plugin.py:272 +msgid "The \"owner\" capability can't be added in the bot. Use the supybot-adduser program (or edit the users.conf file yourself) to add an owner capability." msgstr "" -#: plugin.py:282 +#: plugin.py:283 msgid "You can't add capabilities you don't have." msgstr "" -#: plugin.py:287 +#: plugin.py:288 #, docstring msgid "" " \n" @@ -138,15 +138,15 @@ msgid "" " " msgstr "" -#: plugin.py:299 +#: plugin.py:300 msgid "That user doesn't have that capability." msgstr "" -#: plugin.py:301 +#: plugin.py:302 msgid "You can't remove capabilities you don't have." msgstr "" -#: plugin.py:309 +#: plugin.py:310 #, docstring msgid "" " []\n" @@ -158,7 +158,7 @@ msgid "" " " msgstr "" -#: plugin.py:322 +#: plugin.py:323 #, docstring msgid "" "\n" @@ -168,11 +168,11 @@ msgid "" " " msgstr "" -#: plugin.py:331 +#: plugin.py:332 msgid "%s wasn't in the ignores database." msgstr "" -#: plugin.py:336 +#: plugin.py:337 #, docstring msgid "" "takes no arguments\n" @@ -181,7 +181,7 @@ msgid "" " " msgstr "" -#: plugin.py:344 +#: plugin.py:345 msgid "I'm not currently globally ignoring anyone." msgstr "" From 5fa6ff7bf85e90c871be61c007f0f46a17dd9f9b Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sat, 16 Oct 2010 10:54:33 +0200 Subject: [PATCH 019/136] Add an accidentaly removed space --- plugins/Admin/messages.pot | 60 +++++++++++++++++++------------------- plugins/Admin/plugin.py | 2 +- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/plugins/Admin/messages.pot b/plugins/Admin/messages.pot index e5833f721..673d31155 100644 --- a/plugins/Admin/messages.pot +++ b/plugins/Admin/messages.pot @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2010-10-16 09:41+CEST\n" +"POT-Creation-Date: 2010-10-16 10:43+CEST\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -15,32 +15,32 @@ msgstr "" "Generated-By: pygettext.py 1.5\n" -#: plugin.py:53 +#: plugin.py:54 #, docstring msgid "Nick/channel temporarily unavailable." msgstr "" -#: plugin.py:71 +#: plugin.py:72 msgid "Cannot join %s, it's full." msgstr "" -#: plugin.py:79 +#: plugin.py:80 msgid "Cannot join %s, I was not invited." msgstr "" -#: plugin.py:87 +#: plugin.py:88 msgid "Cannot join %s, it's banned me." msgstr "" -#: plugin.py:95 +#: plugin.py:96 msgid "Cannot join %s, my keyword was wrong." msgstr "" -#: plugin.py:103 +#: plugin.py:104 msgid "Cannot join %s, I'm not identified with the NickServ." msgstr "" -#: plugin.py:133 +#: plugin.py:134 #, docstring msgid "" " []\n" @@ -50,11 +50,11 @@ msgid "" " " msgstr "" -#: plugin.py:146 +#: plugin.py:147 msgid "I'm already too close to maximum number of channels for this network." msgstr "" -#: plugin.py:155 +#: plugin.py:156 #, docstring msgid "" "takes no arguments\n" @@ -64,27 +64,27 @@ msgid "" " " msgstr "" -#: plugin.py:165 +#: plugin.py:166 msgid "I'm not currently in any channels." msgstr "" -#: plugin.py:171 +#: plugin.py:172 msgid "My connection is restricted, I can't change nicks." msgstr "" -#: plugin.py:178 +#: plugin.py:179 msgid "Someone else is already using that nick." msgstr "" -#: plugin.py:185 +#: plugin.py:186 msgid "That nick is currently banned." msgstr "" -#: plugin.py:192 +#: plugin.py:193 msgid "I can't change nicks, the server said %q." msgstr "" -#: plugin.py:206 +#: plugin.py:207 #, docstring msgid "" "[]\n" @@ -94,7 +94,7 @@ msgid "" " " msgstr "" -#: plugin.py:221 +#: plugin.py:222 #, docstring msgid "" "[] []\n" @@ -106,11 +106,11 @@ msgid "" " " msgstr "" -#: plugin.py:239 +#: plugin.py:240 msgid "I'm not in %s." msgstr "" -#: plugin.py:251 +#: plugin.py:252 #, docstring msgid "" " \n" @@ -120,15 +120,15 @@ msgid "" " " msgstr "" -#: plugin.py:271 -msgid "The \"owner\" capability can't be added in thebot. Use the supybot-adduser program (or edit the users.conf file yourself) to add an owner capability." +#: plugin.py:272 +msgid "The \"owner\" capability can't be added in the bot. Use the supybot-adduser program (or edit the users.conf file yourself) to add an owner capability." msgstr "" -#: plugin.py:282 +#: plugin.py:283 msgid "You can't add capabilities you don't have." msgstr "" -#: plugin.py:287 +#: plugin.py:288 #, docstring msgid "" " \n" @@ -138,15 +138,15 @@ msgid "" " " msgstr "" -#: plugin.py:299 +#: plugin.py:300 msgid "That user doesn't have that capability." msgstr "" -#: plugin.py:301 +#: plugin.py:302 msgid "You can't remove capabilities you don't have." msgstr "" -#: plugin.py:309 +#: plugin.py:310 #, docstring msgid "" " []\n" @@ -158,7 +158,7 @@ msgid "" " " msgstr "" -#: plugin.py:322 +#: plugin.py:323 #, docstring msgid "" "\n" @@ -168,11 +168,11 @@ msgid "" " " msgstr "" -#: plugin.py:331 +#: plugin.py:332 msgid "%s wasn't in the ignores database." msgstr "" -#: plugin.py:336 +#: plugin.py:337 #, docstring msgid "" "takes no arguments\n" @@ -181,7 +181,7 @@ msgid "" " " msgstr "" -#: plugin.py:344 +#: plugin.py:345 msgid "I'm not currently globally ignoring anyone." msgstr "" diff --git a/plugins/Admin/plugin.py b/plugins/Admin/plugin.py index 205856a2d..0590f715c 100644 --- a/plugins/Admin/plugin.py +++ b/plugins/Admin/plugin.py @@ -269,7 +269,7 @@ class Admin(callbacks.Plugin): # will depend on supybot.capabilities and its child default) but # generally means they can't mess with channel capabilities. if ircutils.strEqual(capability, 'owner'): - irc.error(_('The "owner" capability can\'t be added in the' + irc.error(_('The "owner" capability can\'t be added in the ' 'bot. Use the supybot-adduser program (or edit the ' 'users.conf file yourself) to add an owner ' 'capability.')) From ac1c7790f5255d94f2eafa3c43df2e217728e458 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sat, 16 Oct 2010 10:59:50 +0200 Subject: [PATCH 020/136] Localized Admin to French --- plugins/Admin/locale/fr.po | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/Admin/locale/fr.po b/plugins/Admin/locale/fr.po index 872ce6191..88c6d002e 100644 --- a/plugins/Admin/locale/fr.po +++ b/plugins/Admin/locale/fr.po @@ -19,11 +19,11 @@ msgstr "Nick/canal temporairement indisponible" #: plugin.py:72 msgid "Cannot join %s, it's full." -msgstr "Ne peut joindre %s, il est plein" +msgstr "Ne peut joindre %s, il est plein." #: plugin.py:80 msgid "Cannot join %s, I was not invited." -msgstr "Ne peut joindre %s, pas invité" +msgstr "Ne peut joindre %s, pas invité." #: plugin.py:88 msgid "Cannot join %s, it's banned me." From e556fbde2e1f888ce79b19de30ae7e788ab7d75a Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sat, 16 Oct 2010 11:24:08 +0200 Subject: [PATCH 021/136] Added %S to format() --- src/utils/str.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/utils/str.py b/src/utils/str.py index c6d801ce7..a7fe09aa9 100644 --- a/src/utils/str.py +++ b/src/utils/str.py @@ -362,7 +362,7 @@ def timestamp(t): t = time.time() return time.ctime(t) -_formatRe = re.compile('%((?:\d+)?\.\d+f|[bfhiLnpqrstu%])') +_formatRe = re.compile('%((?:\d+)?\.\d+f|[bfhiLnpqrsStu%])') def format(s, *args, **kwargs): """w00t. @@ -377,6 +377,7 @@ def format(s, *args, **kwargs): p: pluralize (takes a string) q: quoted (takes a string) n: nItems (takes a 2-tuple of (n, item) or a 3-tuple of (n, between, item)) + S: returns a human-readable size (takes an int) t: time, formatted (takes an int) u: url, wrapped in braces (this should be configurable at some point) """ @@ -425,6 +426,15 @@ def format(s, *args, **kwargs): return nItems(t[0], t[2], between=t[1]) else: raise ValueError, 'Invalid value for %%n in format: %s' % t + elif char == 'S': + t = args.pop() + if not isinstance(t, (int, long)): + raise ValueError, 'Invalid value for %%S in format: %s' % t + for suffix in ['B','KB','MB','GB','TB']: + if t < 1024: + return "%i%s" % (t, suffix) + t /= 1024 + elif char == 't': return timestamp(args.pop()) elif char == 'u': From b47c7c92a391fb7368cd28b284146f78dc08f9e4 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sat, 16 Oct 2010 11:37:58 +0200 Subject: [PATCH 022/136] Edit Status and Web plugins in order to use %S --- plugins/Status/plugin.py | 16 ++++++++-------- plugins/Status/test.py | 3 ++- plugins/Web/plugin.py | 8 ++++---- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/plugins/Status/plugin.py b/plugins/Status/plugin.py index fccd58336..ec7e34487 100644 --- a/plugins/Status/plugin.py +++ b/plugins/Status/plugin.py @@ -104,11 +104,11 @@ class Status(callbacks.Plugin): timeElapsed = utils.timeElapsed(elapsed) except KeyError: timeElapsed = 'an indeterminate amount of time' - irc.reply('I have received %s messages for a total of %s bytes. ' - 'I have sent %s messages for a total of %s bytes. ' - 'I have been connected to %s for %s.' % - (self.recvdMsgs, self.recvdBytes, - self.sentMsgs, self.sentBytes, irc.server, timeElapsed)) + irc.reply(format('I have received %s messages for a total of %S. ' + 'I have sent %s messages for a total of %S. ' + 'I have been connected to %s for %s.', + self.recvdMsgs, self.recvdBytes, + self.sentMsgs, self.sentBytes, irc.server, timeElapsed)) net = wrap(net) def cpu(self, irc, msg, args): @@ -154,10 +154,10 @@ class Status(callbacks.Plugin): irc.error('Unable to run ps command.', Raise=True) (out, _) = inst.communicate() inst.wait() - mem = out.splitlines()[1] + mem = int(out.splitlines()[1]) elif sys.platform.startswith('netbsd'): - mem = '%s kB' % os.stat('/proc/%s/mem' % pid)[7] - response += ' I\'m taking up %s kB of memory.' % mem + mem = int(os.stat('/proc/%s/mem' % pid)[7]) + response += format(' I\'m taking up %S of memory.', mem) except Exception: self.log.exception('Uncaught exception in cpu.memory:') irc.reply(utils.str.normalizeWhitespace(response)) diff --git a/plugins/Status/test.py b/plugins/Status/test.py index 4843c4145..d6781dfab 100644 --- a/plugins/Status/test.py +++ b/plugins/Status/test.py @@ -44,7 +44,8 @@ class StatusTestCase(PluginTestCase): self.failIf('None' in m.args[1], 'None in cpu output: %r.' % m) for s in ['linux', 'freebsd', 'openbsd', 'netbsd', 'darwin']: if sys.platform.startswith(s): - self.failUnless('kB' in m.args[1], + self.failUnless('B' in m.args[1] or 'KB' in m.args[1] or + 'MB' in m.args[1], 'No memory string on supported platform.') try: original = conf.supybot.plugins.Status.cpu.get('children')() diff --git a/plugins/Web/plugin.py b/plugins/Web/plugin.py index 6a332c792..1ff163b37 100644 --- a/plugins/Web/plugin.py +++ b/plugins/Web/plugin.py @@ -149,15 +149,15 @@ class Web(callbacks.PluginRegexp): try: try: size = fd.headers['Content-Length'] - irc.reply(format('%u is %i bytes long.', url, size)) + irc.reply(format('%u is %S long.', url, size)) except KeyError: size = conf.supybot.protocols.http.peekSize() s = fd.read(size) if len(s) != size: - irc.reply(format('%u is %i bytes long.', url, len(s))) + irc.reply(format('%u is %S long.', url, len(s))) else: irc.reply(format('The server didn\'t tell me how long %u ' - 'is but it\'s longer than %i bytes.', + 'is but it\'s longer than %S.', url, size)) finally: fd.close() @@ -182,7 +182,7 @@ class Web(callbacks.PluginRegexp): irc.reply('That URL appears to have no HTML title.') else: irc.reply(format('That URL appears to have no HTML title ' - 'within the first %i bytes.', size)) + 'within the first %S.', size)) title = wrap(title, ['httpUrl']) _netcraftre = re.compile(r'td align="left">\s+]+>(.*?) Date: Sat, 16 Oct 2010 13:51:27 +0200 Subject: [PATCH 023/136] Internationalize Config, Network, Plugin, and User --- plugins/Config/config.py | 2 + plugins/Config/messages.pot | 134 ++++++++++++++++ plugins/Config/plugin.py | 35 +++-- plugins/Network/config.py | 2 + plugins/Network/messages.pot | 170 +++++++++++++++++++++ plugins/Network/plugin.py | 51 ++++--- plugins/Plugin/config.py | 2 + plugins/Plugin/messages.pot | 217 ++++++++++++++++++++++++++ plugins/Plugin/plugin.py | 64 ++++---- plugins/User/config.py | 2 + plugins/User/messages.pot | 288 +++++++++++++++++++++++++++++++++++ plugins/User/plugin.py | 79 ++++++---- 12 files changed, 958 insertions(+), 88 deletions(-) create mode 100644 plugins/Config/messages.pot create mode 100644 plugins/Network/messages.pot create mode 100644 plugins/Plugin/messages.pot create mode 100644 plugins/User/messages.pot diff --git a/plugins/Config/config.py b/plugins/Config/config.py index 878b5051a..7f53e8208 100644 --- a/plugins/Config/config.py +++ b/plugins/Config/config.py @@ -30,6 +30,8 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Config') def configure(advanced): # This will be called by supybot to configure this module. advanced is diff --git a/plugins/Config/messages.pot b/plugins/Config/messages.pot new file mode 100644 index 000000000..bc578f015 --- /dev/null +++ b/plugins/Config/messages.pot @@ -0,0 +1,134 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-16 12:34+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: plugin.py:136 +#, docstring +msgid "" +"\n" +"\n" +" Returns the configuration variables available under the given\n" +" configuration . If a variable has values under it, it is\n" +" preceded by an '@' sign. If a variable is a 'ChannelValue', that is,\n" +" it can be separately configured for each channel using the 'channel'\n" +" command in this plugin, it is preceded by an '#' sign.\n" +" " +msgstr "" + +#: plugin.py:148 +msgid "There don't seem to be any values in %s." +msgstr "" + +#: plugin.py:154 +#, docstring +msgid "" +"\n" +"\n" +" Searches for in the current configuration variables.\n" +" " +msgstr "" + +#: plugin.py:167 +msgid "There were no matching configuration variables." +msgstr "" + +#: plugin.py:174 +msgid "Global: %s; %s: %s" +msgstr "" + +#: plugin.py:185 +msgid "That registry variable has no value. Use the list command in this plugin to see what variables are available in this group." +msgstr "" + +#: plugin.py:200 +#, docstring +msgid "" +"[] []\n" +"\n" +" If is given, sets the channel configuration variable for \n" +" to for . Otherwise, returns the current channel\n" +" configuration value of . is only necessary if the\n" +" message isn't sent in the channel itself." +msgstr "" + +#: plugin.py:207 +msgid "That configuration variable is not a channel-specific configuration variable." +msgstr "" + +#: plugin.py:220 +#, docstring +msgid "" +" []\n" +"\n" +" If is given, sets the value of to . Otherwise,\n" +" returns the current value of . You may omit the leading\n" +" \"supybot.\" in the name if you so choose.\n" +" " +msgstr "" + +#: plugin.py:234 +#, docstring +msgid "" +"\n" +"\n" +" Returns the description of the configuration variable .\n" +" " +msgstr "" + +#: plugin.py:242 +msgid " (Current value: %s)" +msgstr "" + +#: plugin.py:245 +msgid "That configuration group exists, but seems to have no help. Try \"config list %s\" to see if it has any children values." +msgstr "" + +#: plugin.py:249 +msgid "%s has no help." +msgstr "" + +#: plugin.py:254 +#, docstring +msgid "" +"\n" +"\n" +" Returns the default value of the configuration variable .\n" +" " +msgstr "" + +#: plugin.py:264 +#, docstring +msgid "" +"takes no arguments\n" +"\n" +" Reloads the various configuration files (user database, channel\n" +" database, registry, etc.).\n" +" " +msgstr "" + +#: plugin.py:275 +#, docstring +msgid "" +"\n" +"\n" +" Exports the public variables of your configuration to .\n" +" If you want to show someone your configuration file, but you don't\n" +" want that person to be able to see things like passwords, etc., this\n" +" command will export a \"sanitized\" configuration file suitable for\n" +" showing publicly.\n" +" " +msgstr "" + diff --git a/plugins/Config/plugin.py b/plugins/Config/plugin.py index 0b2dd6fda..6ad3bdf8e 100644 --- a/plugins/Config/plugin.py +++ b/plugins/Config/plugin.py @@ -41,6 +41,8 @@ from supybot.utils.iter import all import supybot.ircutils as ircutils import supybot.registry as registry import supybot.callbacks as callbacks +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Config') ### # Now, to setup the registry. @@ -129,6 +131,7 @@ class Config(callbacks.Plugin): utils.sortBy(str.lower, L) return L + @internationalizeDocstring def list(self, irc, msg, args, group): """ @@ -142,9 +145,11 @@ class Config(callbacks.Plugin): if L: irc.reply(format('%L', L)) else: - irc.error('There don\'t seem to be any values in %s.' % group._name) + irc.error(_('There don\'t seem to be any values in %s.') % + group._name) list = wrap(list, ['configVar']) + @internationalizeDocstring def search(self, irc, msg, args, word): """ @@ -159,14 +164,14 @@ class Config(callbacks.Plugin): if L: irc.reply(format('%L', L)) else: - irc.reply('There were no matching configuration variables.') + irc.reply(_('There were no matching configuration variables.')) search = wrap(search, ['lowered']) # XXX compose with withoutSpaces? def _getValue(self, irc, msg, group, addChannel=False): value = str(group) or ' ' if addChannel and irc.isChannel(msg.args[0]) and not irc.nested: s = str(group.get(msg.args[0])) - value = 'Global: %s; %s: %s' % (value, msg.args[0], s) + value = _('Global: %s; %s: %s') % (value, msg.args[0], s) if hasattr(group, 'value'): if not group._private: irc.reply(value) @@ -177,9 +182,9 @@ class Config(callbacks.Plugin): else: irc.errorNoCapability(capability) else: - irc.error('That registry variable has no value. Use the list ' + irc.error(_('That registry variable has no value. Use the list ' 'command in this plugin to see what variables are ' - 'available in this group.') + 'available in this group.')) def _setValue(self, irc, msg, group, value): capability = getCapability(group._name) @@ -190,6 +195,7 @@ class Config(callbacks.Plugin): else: irc.errorNoCapability(capability) + @internationalizeDocstring def channel(self, irc, msg, args, channel, group, value): """[] [] @@ -198,8 +204,8 @@ class Config(callbacks.Plugin): configuration value of . is only necessary if the message isn't sent in the channel itself.""" if not group.channelValue: - irc.error('That configuration variable is not a channel-specific ' - 'configuration variable.') + irc.error(_('That configuration variable is not a channel-specific ' + 'configuration variable.')) return group = group.get(channel) if value is not None: @@ -209,6 +215,7 @@ class Config(callbacks.Plugin): channel = wrap(channel, ['channel', 'settableConfigVar', additional('text')]) + @internationalizeDocstring def config(self, irc, msg, args, group, value): """ [] @@ -222,6 +229,7 @@ class Config(callbacks.Plugin): self._getValue(irc, msg, group, addChannel=group.channelValue) config = wrap(config, ['settableConfigVar', additional('text')]) + @internationalizeDocstring def help(self, irc, msg, args, group): """ @@ -231,16 +239,17 @@ class Config(callbacks.Plugin): s = group.help() if s: if hasattr(group, 'value') and not group._private: - s += ' (Current value: %s)' % group + s += _(' (Current value: %s)') % group irc.reply(s) else: - irc.reply('That configuration group exists, but seems to have ' - 'no help. Try "config list %s" to see if it has ' - 'any children values.' % group._name) + irc.reply(_('That configuration group exists, but seems to ' + 'have no help. Try "config list %s" to see if it ' + 'has any children values.') % group._name) else: - irc.error('%s has no help.' % group._name) + irc.error(_('%s has no help.') % group._name) help = wrap(help, ['configVar']) + @internationalizeDocstring def default(self, irc, msg, args, group): """ @@ -250,6 +259,7 @@ class Config(callbacks.Plugin): irc.reply(str(v)) default = wrap(default, ['settableConfigVar']) + @internationalizeDocstring def reload(self, irc, msg, args): """takes no arguments @@ -260,6 +270,7 @@ class Config(callbacks.Plugin): irc.replySuccess() reload = wrap(reload, [('checkCapability', 'owner')]) + @internationalizeDocstring def export(self, irc, msg, args, filename): """ diff --git a/plugins/Network/config.py b/plugins/Network/config.py index f50ddc473..c1cf102ef 100644 --- a/plugins/Network/config.py +++ b/plugins/Network/config.py @@ -29,6 +29,8 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Network') def configure(advanced): # This will be called by supybot to configure this module. advanced is diff --git a/plugins/Network/messages.pot b/plugins/Network/messages.pot new file mode 100644 index 000000000..e368ae1f0 --- /dev/null +++ b/plugins/Network/messages.pot @@ -0,0 +1,170 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-16 12:52+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: plugin.py:57 +#, docstring +msgid "" +"[--ssl] [] []\n" +"\n" +" Connects to another network (which will be represented by the name\n" +" provided in ) at . If port is not provided, it\n" +" defaults to 6667, the default port for IRC. If password is\n" +" provided, it will be sent to the server in a PASS command. If --ssl is\n" +" provided, an SSL connection will be attempted.\n" +" " +msgstr "" + +#: plugin.py:67 +msgid "I'm already connected to %s." +msgstr "" + +#: plugin.py:87 +msgid "A server must be provided if the network is not already registered." +msgstr "" + +#: plugin.py:95 +msgid "Connection to %s initiated." +msgstr "" + +#: plugin.py:102 +#, docstring +msgid "" +"[] []\n" +"\n" +" Disconnects from the network represented by the network .\n" +" If is given, quits the network with the given quit\n" +" message. is only necessary if the network is different\n" +" from the network the command is sent on.\n" +" " +msgstr "" + +#: plugin.py:114 +msgid "Disconnection to %s initiated." +msgstr "" + +#: plugin.py:120 +#, docstring +msgid "" +"[] []\n" +"\n" +" Disconnects and then reconnects to . If no network is given,\n" +" disconnects and then reconnects to the network the command was given\n" +" on. If no quit message is given, uses the configured one\n" +" (supybot.plugins.Owner.quitMsg) or the nick of the person giving the\n" +" command.\n" +" " +msgstr "" + +#: plugin.py:137 +#, docstring +msgid "" +" [ ...]\n" +"\n" +" Gives the bot (with its associated s) on .\n" +" " +msgstr "" + +#: plugin.py:210 +msgid "is an op on %L" +msgstr "" + +#: plugin.py:212 +msgid "is a halfop on %L" +msgstr "" + +#: plugin.py:214 +msgid "is voiced on %L" +msgstr "" + +#: plugin.py:217 +msgid "is also on %L" +msgstr "" + +#: plugin.py:219 +msgid "is on %L" +msgstr "" + +#: plugin.py:221 +msgid "isn't on any non-secret channels" +msgstr "" + +#: plugin.py:228 plugin.py:229 plugin.py:233 +msgid "" +msgstr "" + +#: plugin.py:240 +msgid " identified" +msgstr "" + +#: plugin.py:245 +msgid "%s (%s) has been%s on server %s since %s (idle for %s) and %s.%s" +msgstr "" + +#: plugin.py:258 +msgid "There is no %s on %s." +msgstr "" + +#: plugin.py:264 +#, docstring +msgid "" +"[] \n" +"\n" +" Returns the WHOIS response gives for . is\n" +" only necessary if the network is different than the network the command\n" +" is sent on.\n" +" " +msgstr "" + +#: plugin.py:280 +#, docstring +msgid "" +"takes no arguments\n" +"\n" +" Returns the networks to which the bot is currently connected.\n" +" " +msgstr "" + +#: plugin.py:293 +msgid "%.2f seconds." +msgstr "" + +#: plugin.py:297 +#, docstring +msgid "" +"[]\n" +"\n" +" Returns the current latency to . is only necessary\n" +" if the message isn't sent on the network to which this command is to\n" +" apply.\n" +" " +msgstr "" + +#: plugin.py:303 +msgid "Latency check (from %s)." +msgstr "" + +#: plugin.py:311 +#, docstring +msgid "" +"[]\n" +"\n" +" Returns the current network driver for . is only\n" +" necessary if the message isn't sent on the network to which this\n" +" command is to apply.\n" +" " +msgstr "" + diff --git a/plugins/Network/plugin.py b/plugins/Network/plugin.py index 2eb405f01..41bf0c646 100644 --- a/plugins/Network/plugin.py +++ b/plugins/Network/plugin.py @@ -38,6 +38,8 @@ import supybot.ircmsgs as ircmsgs import supybot.ircutils as ircutils import supybot.registry as registry import supybot.callbacks as callbacks +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Network') class Network(callbacks.Plugin): _whois = {} @@ -50,6 +52,7 @@ class Network(callbacks.Plugin): raise callbacks.Error, \ 'I\'m not currently connected to %s.' % network + @internationalizeDocstring def connect(self, irc, msg, args, opts, network, server, password): """[--ssl] [] [] @@ -61,7 +64,7 @@ class Network(callbacks.Plugin): """ try: otherIrc = self._getIrc(network) - irc.error('I\'m already connected to %s.' % network) + irc.error(_('I\'m already connected to %s.') % network) return # We've gotta return here. This is ugly code, but I'm not # quite sure what to do about it. except callbacks.Error: @@ -81,19 +84,20 @@ class Network(callbacks.Plugin): try: serverPort = conf.supybot.networks.get(network).servers()[0] except (registry.NonExistentRegistryEntry, IndexError): - irc.error('A server must be provided if the network is not ' - 'already registered.') + irc.error(_('A server must be provided if the network is not ' + 'already registered.')) return Owner = irc.getCallback('Owner') newIrc = Owner._connect(network, serverPort=serverPort, password=password, ssl=ssl) conf.supybot.networks().add(network) assert newIrc.callbacks is irc.callbacks, 'callbacks list is different' - irc.replySuccess('Connection to %s initiated.' % network) + irc.replySuccess(_('Connection to %s initiated.') % network) connect = wrap(connect, ['owner', getopts({'ssl': ''}), 'something', additional('something'), additional('something', '')]) + @internationalizeDocstring def disconnect(self, irc, msg, args, otherIrc, quitMsg): """[] [] @@ -107,10 +111,11 @@ class Network(callbacks.Plugin): otherIrc.die() conf.supybot.networks().discard(otherIrc.network) if otherIrc != irc: - irc.replySuccess('Disconnection to %s initiated.' % + irc.replySuccess(_('Disconnection to %s initiated.') % otherIrc.network) disconnect = wrap(disconnect, ['owner', 'networkIrc', additional('text')]) + @internationalizeDocstring def reconnect(self, irc, msg, args, otherIrc, quitMsg): """[] [] @@ -127,6 +132,7 @@ class Network(callbacks.Plugin): irc.replySuccess() reconnect = wrap(reconnect, ['owner', 'networkIrc', additional('text')]) + @internationalizeDocstring def command(self, irc, msg, args, otherIrc, commandAndArgs): """ [ ...] @@ -201,43 +207,43 @@ class Network(callbacks.Plugin): voices.append(channel[1:]) L = [] if ops: - L.append(format('is an op on %L', ops)) + L.append(format(_('is an op on %L'), ops)) if halfops: - L.append(format('is a halfop on %L', halfops)) + L.append(format(_('is a halfop on %L'), halfops)) if voices: - L.append(format('is voiced on %L', voices)) + L.append(format(_('is voiced on %L'), voices)) if normal: if L: - L.append(format('is also on %L', normal)) + L.append(format(_('is also on %L'), normal)) else: - L.append(format('is on %L', normal)) + L.append(format(_('is on %L'), normal)) else: - L = ['isn\'t on any non-secret channels'] + L = [_('isn\'t on any non-secret channels')] channels = format('%L', L) if '317' in d: idle = utils.timeElapsed(d['317'].args[2]) signon = time.strftime(conf.supybot.reply.format.time(), time.localtime(float(d['317'].args[3]))) else: - idle = '' - signon = '' + idle = _('') + signon = _('') if '312' in d: server = d['312'].args[2] else: - server = '' + server = _('') if '301' in d: away = ' %s is away: %s.' % (nick, d['301'].args[2]) else: away = '' if '320' in d: if d['320'].args[2]: - identify = ' identified' + identify = _(' identified') else: identify = '' else: identify = '' - s = '%s (%s) has been%s on server %s since %s (idle for %s) and ' \ - '%s.%s' % (user, hostmask, identify, server, signon, idle, + s = _('%s (%s) has been%s on server %s since %s (idle for %s) and ' + '%s.%s') % (user, hostmask, identify, server, signon, idle, channels, away) replyIrc.reply(s) del self._whois[(irc, loweredNick)] @@ -249,10 +255,11 @@ class Network(callbacks.Plugin): return (replyIrc, replyMsg, d) = self._whois[(irc, loweredNick)] del self._whois[(irc, loweredNick)] - s = 'There is no %s on %s.' % (nick, irc.network) + s = _('There is no %s on %s.') % (nick, irc.network) replyIrc.reply(s) do401 = do402 + @internationalizeDocstring def whois(self, irc, msg, args, otherIrc, nick): """[] @@ -268,6 +275,7 @@ class Network(callbacks.Plugin): self._whois[(otherIrc, nick)] = (irc, msg, {}) whois = wrap(whois, ['networkIrc', 'nick']) + @internationalizeDocstring def networks(self, irc, msg, args): """takes no arguments @@ -282,8 +290,9 @@ class Network(callbacks.Plugin): now = time.time() if irc in self._latency: (replyIrc, when) = self._latency.pop(irc) - replyIrc.reply('%.2f seconds.' % (now-when)) + replyIrc.reply(_('%.2f seconds.') % (now-when)) + @internationalizeDocstring def latency(self, irc, msg, args, otherIrc): """[] @@ -291,11 +300,13 @@ class Network(callbacks.Plugin): if the message isn't sent on the network to which this command is to apply. """ - otherIrc.queueMsg(ircmsgs.ping('Latency check (from %s).' % msg.nick)) + otherIrc.queueMsg(ircmsgs.ping(_('Latency check (from %s).') % + msg.nick)) self._latency[otherIrc] = (irc, time.time()) irc.noReply() latency = wrap(latency, ['networkIrc']) + @internationalizeDocstring def driver(self, irc, msg, args, otherIrc): """[] diff --git a/plugins/Plugin/config.py b/plugins/Plugin/config.py index 3a0891ace..f9d0cd673 100644 --- a/plugins/Plugin/config.py +++ b/plugins/Plugin/config.py @@ -29,6 +29,8 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Plugin') def configure(advanced): # This will be called by supybot to configure this module. advanced is diff --git a/plugins/Plugin/messages.pot b/plugins/Plugin/messages.pot new file mode 100644 index 000000000..3e978d574 --- /dev/null +++ b/plugins/Plugin/messages.pot @@ -0,0 +1,217 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-16 13:50+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: plugin.py:43 +#, docstring +msgid "" +"This plugin exists to help users manage their plugins. Use 'plugin\n" +" list' to list the loaded plugins; use 'plugin help' to get the description\n" +" of a plugin; use the 'plugin' command itself to determine what plugin a\n" +" command exists in." +msgstr "" + +#: plugin.py:49 +#, docstring +msgid "" +"\n" +"\n" +" Returns a useful description of how to use , if the plugin has\n" +" one.\n" +" " +msgstr "" + +#: plugin.py:58 +msgid "That plugin is loaded, but has no plugin help." +msgstr "" + +#: plugin.py:63 +#, docstring +msgid "" +"takes no arguments\n" +"\n" +" Returns a list of the currently loaded plugins.\n" +" " +msgstr "" + +#: plugin.py:74 +#, docstring +msgid "" +"\n" +"\n" +" Returns the plugin(s) that is in.\n" +" " +msgstr "" + +#: plugin.py:89 +msgid "plugins" +msgstr "" + +#: plugin.py:91 +msgid "plugin" +msgstr "" + +#: plugin.py:92 +msgid "The %q command is available in the %L %s." +msgstr "" + +#: plugin.py:95 +msgid "There is no command %q." +msgstr "" + +#: plugin.py:100 +#, docstring +msgid "" +"\n" +"\n" +" Returns the author of . This is the person you should talk to\n" +" if you have ideas, suggestions, or other comments about a given plugin.\n" +" " +msgstr "" + +#: plugin.py:106 +msgid "That plugin does not seem to be loaded." +msgstr "" + +#: plugin.py:112 +msgid "That plugin doesn't have an author that claims it." +msgstr "" + +#: plugin.py:117 +#, docstring +msgid "" +" []\n" +"\n" +" Replies with a list of people who made contributions to a given plugin.\n" +" If is specified, that person's specific contributions will\n" +" be listed. Note: The is the part inside of the parentheses\n" +" in the people listing.\n" +" " +msgstr "" + +#: plugin.py:125 +#, docstring +msgid "" +"\n" +" Take an Authors object, and return only the name and nick values\n" +" in the format 'First Last (nick)'.\n" +" " +msgstr "" + +#: plugin.py:131 +#, docstring +msgid "" +"\n" +" Take a list of long names and turn it into :\n" +" shortname[, shortname and shortname].\n" +" " +msgstr "" + +#: plugin.py:138 +#, docstring +msgid "" +"\n" +" Sort the list of 'long names' based on the number of contributions\n" +" associated with each.\n" +" " +msgstr "" + +#: plugin.py:148 +#, docstring +msgid "" +"\n" +" Build the list of author + contributors (if any) for the requested\n" +" plugin.\n" +" " +msgstr "" + +#: plugin.py:152 +msgid "The %s plugin" +msgstr "" + +#: plugin.py:153 +msgid "has not been claimed by an author" +msgstr "" + +#: plugin.py:154 +msgid "and" +msgstr "" + +#: plugin.py:155 +msgid "has no contributors listed." +msgstr "" + +#: plugin.py:160 +msgid "was written by %s" +msgstr "" + +#: plugin.py:171 +msgid "%s %h contributed to it." +msgstr "" + +#: plugin.py:176 +msgid "has no additional contributors listed." +msgstr "" + +#: plugin.py:178 +msgid "but" +msgstr "" + +#: plugin.py:181 +#, docstring +msgid "" +"\n" +" Build the list of contributions (if any) for the requested person\n" +" for the requested plugin\n" +" " +msgstr "" + +#: plugin.py:195 +msgid "The nick specified (%s) is not a registered contributor." +msgstr "" + +#: plugin.py:201 +msgid "The %s plugin does not have '%s' listed as a contributor." +msgstr "" + +#: plugin.py:209 +msgid "command" +msgstr "" + +#: plugin.py:212 +msgid "the %L %s" +msgstr "" + +#: plugin.py:214 +msgid "the %L" +msgstr "" + +#: plugin.py:217 +msgid "%s wrote the %s plugin and also contributed %L." +msgstr "" + +#: plugin.py:220 +msgid "%s contributed %L to the %s plugin." +msgstr "" + +#: plugin.py:223 +msgid "%s wrote the %s plugin" +msgstr "" + +#: plugin.py:226 +msgid "%s has no listed contributions for the %s plugin." +msgstr "" + diff --git a/plugins/Plugin/plugin.py b/plugins/Plugin/plugin.py index 6753125ce..0e77eab3d 100644 --- a/plugins/Plugin/plugin.py +++ b/plugins/Plugin/plugin.py @@ -34,13 +34,17 @@ from supybot.commands import * import supybot.plugins as plugins import supybot.ircutils as ircutils import supybot.callbacks as callbacks +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Plugin') +@internationalizeDocstring class Plugin(callbacks.Plugin): """This plugin exists to help users manage their plugins. Use 'plugin list' to list the loaded plugins; use 'plugin help' to get the description of a plugin; use the 'plugin' command itself to determine what plugin a command exists in.""" + @internationalizeDocstring def help(self, irc, msg, args, cb): """ @@ -51,9 +55,10 @@ class Plugin(callbacks.Plugin): if doc: irc.reply(utils.str.normalizeWhitespace(doc)) else: - irc.reply('That plugin is loaded, but has no plugin help.') + irc.reply(_('That plugin is loaded, but has no plugin help.')) help = wrap(help, ['plugin']) + @internationalizeDocstring def list(self, irc, msg, args): """takes no arguments @@ -64,6 +69,7 @@ class Plugin(callbacks.Plugin): irc.reply(format('%L', L)) list = wrap(list) + @internationalizeDocstring def plugin(self, irc, msg, args, command): """ @@ -80,15 +86,16 @@ class Plugin(callbacks.Plugin): irc.reply(format('%L', L)) else: if len(L) > 1: - plugin = 'plugins' + plugin = _('plugins') else: - plugin = 'plugin' - irc.reply(format('The %q command is available in the %L %s.', - command, L, plugin)) + plugin = _('plugin') + irc.reply(format(_('The %q command is available in the %L ' + '%s.'), command, L, plugin)) else: - irc.error(format('There is no command %q.', command)) + irc.error(format(_('There is no command %q.'), command)) plugin = wrap(plugin, [many('something')]) + @internationalizeDocstring def author(self, irc, msg, args, cb): """ @@ -96,15 +103,16 @@ class Plugin(callbacks.Plugin): if you have ideas, suggestions, or other comments about a given plugin. """ if cb is None: - irc.error('That plugin does not seem to be loaded.') + irc.error(_('That plugin does not seem to be loaded.')) return module = cb.classModule if hasattr(module, '__author__') and module.__author__: irc.reply(str(module.__author__)) else: - irc.reply('That plugin doesn\'t have an author that claims it.') + irc.reply(_('That plugin doesn\'t have an author that claims it.')) author = wrap(author, [('plugin')]) + @internationalizeDocstring def contributors(self, irc, msg, args, cb, nick): """ [] @@ -141,15 +149,15 @@ class Plugin(callbacks.Plugin): Build the list of author + contributors (if any) for the requested plugin. """ - head = 'The %s plugin' % cb.name() - author = 'has not been claimed by an author' - conjunction = 'and' - contrib = 'has no contributors listed.' + head = _('The %s plugin') % cb.name() + author = _('has not been claimed by an author') + conjunction = _('and') + contrib = _('has no contributors listed.') hasAuthor = False hasContribs = False if hasattr(module, '__author__'): if module.__author__ != supybot.authors.unknown: - author = 'was written by %s' % \ + author = _('was written by %s') % \ utils.web.mungeEmail(str(module.__author__)) hasAuthor = True if hasattr(module, '__contributors__'): @@ -160,14 +168,14 @@ class Plugin(callbacks.Plugin): except ValueError: pass if contribs: - contrib = format('%s %h contributed to it.', + contrib = format(_('%s %h contributed to it.'), buildContributorsString(contribs), len(contribs)) hasContribs = True elif hasAuthor: - contrib = 'has no additional contributors listed.' + contrib = _('has no additional contributors listed.') if hasContribs and not hasAuthor: - conjunction = 'but' + conjunction = _('but') return ' '.join([head, author, conjunction, contrib]) def buildPersonString(module): """ @@ -184,39 +192,39 @@ class Plugin(callbacks.Plugin): break authorInfo = authorInfo or getattr(supybot.authors, nick, None) if not authorInfo: - return 'The nick specified (%s) is not a registered ' \ - 'contributor.' % nick + return _('The nick specified (%s) is not a registered ' + 'contributor.') % nick fullName = utils.web.mungeEmail(str(authorInfo)) contributions = [] if hasattr(module, '__contributors__'): if authorInfo not in module.__contributors__: - return 'The %s plugin does not have \'%s\' listed as a ' \ - 'contributor.' % (cb.name(), nick) + return _('The %s plugin does not have \'%s\' listed as a ' + 'contributor.') % (cb.name(), nick) contributions = module.__contributors__[authorInfo] isAuthor = getattr(module, '__author__', False) == authorInfo (nonCommands, commands) = utils.iter.partition(lambda s: ' ' in s, contributions) results = [] if commands: - s = 'command' + s = _('command') if len(commands) > 1: s = utils.str.pluralize(s) - results.append(format('the %L %s', commands, s)) + results.append(format(_('the %L %s'), commands, s)) if nonCommands: - results.append(format('the %L', nonCommands)) + results.append(format(_('the %L'), nonCommands)) if results and isAuthor: return format( - '%s wrote the %s plugin and also contributed %L.', + _('%s wrote the %s plugin and also contributed %L.'), (fullName, cb.name(), results)) elif results and not isAuthor: - return format('%s contributed %L to the %s plugin.', + return format(_('%s contributed %L to the %s plugin.'), fullName, results, cb.name()) elif isAuthor and not results: - return '%s wrote the %s plugin' % (fullName, cb.name()) + return _('%s wrote the %s plugin') % (fullName, cb.name()) # XXX Does this ever actually get reached? else: - return '%s has no listed contributions for the %s plugin.' % \ - (fullName, cb.name()) + return _('%s has no listed contributions for the %s ' + 'plugin.') % (fullName, cb.name()) # First we need to check and see if the requested plugin is loaded module = cb.classModule if not nick: diff --git a/plugins/User/config.py b/plugins/User/config.py index 7cc8b8f24..eb6bea839 100644 --- a/plugins/User/config.py +++ b/plugins/User/config.py @@ -30,6 +30,8 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('User') def configure(advanced): # This will be called by supybot to configure this module. advanced is diff --git a/plugins/User/messages.pot b/plugins/User/messages.pot new file mode 100644 index 000000000..faaf3ad96 --- /dev/null +++ b/plugins/User/messages.pot @@ -0,0 +1,288 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-16 13:50+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: plugin.py:49 +#, docstring +msgid "" +"[--capability=] []\n" +"\n" +" Returns the valid registered usernames matching . If is\n" +" not given, returns all registered usernames.\n" +" " +msgstr "" + +#: plugin.py:80 +msgid "There are no matching registered users." +msgstr "" + +#: plugin.py:82 +msgid "There are no registered users." +msgstr "" + +#: plugin.py:88 +#, docstring +msgid "" +" \n" +"\n" +" Registers with the given password and the current\n" +" hostmask of the person registering. You shouldn't register twice; if\n" +" you're not recognized as a user but you've already registered, use the\n" +" hostmask add command to add another hostmask to your already-registered\n" +" user, or use the identify command to identify just for a session.\n" +" This command (and all other commands that include a password) must be\n" +" sent to the bot privately, not in a channel.\n" +" " +msgstr "" + +#: plugin.py:101 +msgid "That name is already assigned to someone." +msgstr "" + +#: plugin.py:106 +msgid "username" +msgstr "" + +#: plugin.py:107 +msgid "Hostmasks are not valid usernames." +msgstr "" + +#: plugin.py:114 +msgid "Your hostmask is already registered to %s" +msgstr "" + +#: plugin.py:130 +#, docstring +msgid "" +" []\n" +"\n" +" Unregisters from the user database. If the user giving this\n" +" command is an owner user, the password is not necessary.\n" +" " +msgstr "" + +#: plugin.py:145 +msgid "This command has been disabled. You'll have to ask the owner of this bot to unregister your user." +msgstr "" + +#: plugin.py:158 +#, docstring +msgid "" +" []\n" +"\n" +" Changes your current user database name to the new name given.\n" +" is only necessary if the user isn't recognized by hostmask.\n" +" This message must be sent to the bot privately (not on a channel) since\n" +" it may contain a password.\n" +" " +msgstr "" + +#: plugin.py:167 +msgid "%q is already registered." +msgstr "" + +#: plugin.py:181 +#, docstring +msgid "" +"[] \n" +"\n" +" Sets the new password for the user specified by to . Obviously this message must be sent to the bot\n" +" privately (not in a channel). If the requesting user is an owner\n" +" user (and the user whose password is being changed isn't that same\n" +" owner user), then needn't be correct.\n" +" " +msgstr "" + +#: plugin.py:209 +#, docstring +msgid "" +" []\n" +"\n" +" Sets the secure flag on the user of the person sending the message.\n" +" Requires that the person's hostmask be in the list of hostmasks for\n" +" that user in addition to the password being correct. When the\n" +" secure flag is set, the user *must* identify before he can be\n" +" recognized. If a specific True/False value is not given, it\n" +" inverts the current value.\n" +" " +msgstr "" + +#: plugin.py:224 +msgid "Secure flag set to %s" +msgstr "" + +#: plugin.py:232 +#, docstring +msgid "" +"\n" +"\n" +" Returns the username of the user specified by or if\n" +" the user is registered.\n" +" " +msgstr "" + +#: plugin.py:241 +msgid "I haven't seen %s." +msgstr "" + +#: plugin.py:246 +msgid "I don't know who that is." +msgstr "" + +#: plugin.py:252 +#, docstring +msgid "" +"[]\n" +"\n" +" Returns the hostmask of . If isn't given, return the\n" +" hostmask of the person giving the command.\n" +" " +msgstr "" + +#: plugin.py:264 +#, docstring +msgid "" +"[]\n" +"\n" +" Returns the hostmasks of the user specified by ; if \n" +" isn't specified, returns the hostmasks of the user calling the\n" +" command.\n" +" " +msgstr "" + +#: plugin.py:276 +msgid "%s has no registered hostmasks." +msgstr "" + +#: plugin.py:283 +msgid "You may only retrieve your own (hostmasks." +msgstr "" + +#: plugin.py:299 +#, docstring +msgid "" +"[] [] []\n" +"\n" +" Adds the hostmask to the user specified by . The\n" +" may only be required if the user is not recognized by\n" +" hostmask. is also not required if an owner user is\n" +" giving the command on behalf of some other user. If is\n" +" not given, it defaults to your current hostmask. If is not\n" +" given, it defaults to your currently identified name. This message\n" +" must be sent to the bot privately (not on a channel) since it may\n" +" contain a password.\n" +" " +msgstr "" + +#: plugin.py:313 +msgid "hostmask" +msgstr "" + +#: plugin.py:314 +msgid "Make sure your hostmask includes a nick, then an exclamation point (!), then a user, then an at symbol (@), then a host. Feel free to use wildcards (* and ?, which work just like they do on the command line) in any of these parts." +msgstr "" + +#: plugin.py:324 plugin.py:347 +msgid "That hostmask is already registered." +msgstr "" + +#: plugin.py:355 +#, docstring +msgid "" +" []\n" +"\n" +" Removes the hostmask from the record of the user\n" +" specified by . If the hostmask given is 'all' then all\n" +" hostmasks will be removed. The may only be required if\n" +" the user is not recognized by his hostmask. This message must be\n" +" sent to the bot privately (not on a channel) since it may contain a\n" +" password.\n" +" " +msgstr "" + +#: plugin.py:374 +msgid "All hostmasks removed." +msgstr "" + +#: plugin.py:378 +msgid "There was no such hostmask." +msgstr "" + +#: plugin.py:387 +#, docstring +msgid "" +"[]\n" +"\n" +" Returns the capabilities of the user specified by ; if \n" +" isn't specified, returns the capabilities of the user calling the\n" +" command.\n" +" " +msgstr "" + +#: plugin.py:407 +#, docstring +msgid "" +" \n" +"\n" +" Identifies the user as . This command (and all other\n" +" commands that include a password) must be sent to the bot privately,\n" +" not in a channel.\n" +" " +msgstr "" + +#: plugin.py:419 +msgid "Your secure flag is true and your hostmask doesn't match any of your known hostmasks." +msgstr "" + +#: plugin.py:429 +#, docstring +msgid "" +"takes no arguments\n" +"\n" +" Un-identifies you. Note that this may not result in the desired\n" +" effect of causing the bot not to recognize you anymore, since you may\n" +" have added hostmasks to your user that can cause the bot to continue to\n" +" recognize you.\n" +" " +msgstr "" + +#: plugin.py:447 +#, docstring +msgid "" +"takes no arguments\n" +"\n" +" Returns the name of the user calling the command.\n" +" " +msgstr "" + +#: plugin.py:455 +msgid "I don't recognize you." +msgstr "" + +#: plugin.py:460 +#, docstring +msgid "" +"takes no arguments\n" +"\n" +" Returns some statistics on the user database.\n" +" " +msgstr "" + +#: plugin.py:478 +msgid "I have %s registered users with %s registered hostmasks; %n and %n." +msgstr "" + diff --git a/plugins/User/plugin.py b/plugins/User/plugin.py index e9eddc3b6..f949ac14a 100644 --- a/plugins/User/plugin.py +++ b/plugins/User/plugin.py @@ -36,12 +36,15 @@ import supybot.ircdb as ircdb from supybot.commands import * import supybot.ircutils as ircutils import supybot.callbacks as callbacks +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('User') class User(callbacks.Plugin): def _checkNotChannel(self, irc, msg, password=' '): if password and irc.isChannel(msg.args[0]): raise callbacks.Error, conf.supybot.replies.requiresPrivacy() + @internationalizeDocstring def list(self, irc, msg, args, optlist, glob): """[--capability=] [] @@ -74,12 +77,13 @@ class User(callbacks.Plugin): irc.reply(format('%L', users)) else: if predicates: - irc.reply('There are no matching registered users.') + irc.reply(_('There are no matching registered users.')) else: - irc.reply('There are no registered users.') + irc.reply(_('There are no registered users.')) list = wrap(list, [getopts({'capability':'capability'}), additional('glob')]) + @internationalizeDocstring def register(self, irc, msg, args, name, password): """ @@ -94,18 +98,21 @@ class User(callbacks.Plugin): addHostmask = True try: ircdb.users.getUserId(name) - irc.error('That name is already assigned to someone.', Raise=True) + irc.error(_('That name is already assigned to someone.'), + Raise=True) except KeyError: pass if ircutils.isUserHostmask(name): - irc.errorInvalid('username', name, - 'Hostmasks are not valid usernames.', Raise=True) + irc.errorInvalid(_('username'), name, + _('Hostmasks are not valid usernames.'), + Raise=True) try: u = ircdb.users.getUser(msg.prefix) if u._checkCapability('owner'): addHostmask = False else: - irc.error('Your hostmask is already registered to %s' % u.name) + irc.error(_('Your hostmask is already registered to %s') % + u.name) return except KeyError: pass @@ -118,6 +125,7 @@ class User(callbacks.Plugin): irc.replySuccess() register = wrap(register, ['private', 'something', 'something']) + @internationalizeDocstring def unregister(self, irc, msg, args, user, password): """ [] @@ -134,9 +142,9 @@ class User(callbacks.Plugin): if not caller or not isOwner: self.log.warning('%s tried to unregister user %s.', msg.prefix, user.name) - irc.error('This command has been disabled. You\'ll have to ' - 'ask the owner of this bot to unregister your user.', - Raise=True) + irc.error(_('This command has been disabled. You\'ll have to ' + 'ask the owner of this bot to unregister your ' + 'user.'), Raise=True) if isOwner or user.checkPassword(password): ircdb.users.delUser(user.id) irc.replySuccess() @@ -145,6 +153,7 @@ class User(callbacks.Plugin): unregister = wrap(unregister, ['private', 'otherUser', additional('anything')]) + @internationalizeDocstring def changename(self, irc, msg, args, user, newname, password): """ [] @@ -155,7 +164,7 @@ class User(callbacks.Plugin): """ try: id = ircdb.users.getUserId(newname) - irc.error(format('%q is already registered.', newname)) + irc.error(format(_('%q is already registered.'), newname)) return except KeyError: pass @@ -167,6 +176,7 @@ class User(callbacks.Plugin): additional('something', '')]) class set(callbacks.Commands): + @internationalizeDocstring def password(self, irc, msg, args, user, password, newpassword): """[] @@ -194,6 +204,7 @@ class User(callbacks.Plugin): password = wrap(password, ['private', optional('otherUser'), 'something', 'something']) + @internationalizeDocstring def secure(self, irc, msg, args, user, password, value): """ [] @@ -210,12 +221,13 @@ class User(callbacks.Plugin): user.checkHostmask(msg.prefix, useAuth=False): user.secure = value ircdb.users.setUser(user) - irc.reply('Secure flag set to %s' % value) + irc.reply(_('Secure flag set to %s') % value) else: irc.error(conf.supybot.replies.incorrectAuthentication()) secure = wrap(secure, ['private', 'user', 'something', additional('boolean')]) + @internationalizeDocstring def username(self, irc, msg, args, hostmask): """ @@ -226,15 +238,16 @@ class User(callbacks.Plugin): try: hostmask = irc.state.nickToHostmask(hostmask) except KeyError: - irc.error('I haven\'t seen %s.' % hostmask, Raise=True) + irc.error(_('I haven\'t seen %s.') % hostmask, Raise=True) try: user = ircdb.users.getUser(hostmask) irc.reply(user.name) except KeyError: - irc.error('I don\'t know who that is.') + irc.error(_('I don\'t know who that is.')) username = wrap(username, [first('nick', 'hostmask')]) class hostmask(callbacks.Commands): + @internationalizeDocstring def hostmask(self, irc, msg, args, nick): """[] @@ -246,6 +259,7 @@ class User(callbacks.Plugin): irc.reply(irc.state.nickToHostmask(nick)) hostmask = wrap(hostmask, [additional('seenNick')]) + @internationalizeDocstring def list(self, irc, msg, args, name): """[] @@ -259,14 +273,15 @@ class User(callbacks.Plugin): hostmasks.sort() return format('%L', hostmasks) else: - return format('%s has no registered hostmasks.', user.name) + return format(_('%s has no registered hostmasks.'), + user.name) try: user = ircdb.users.getUser(msg.prefix) if name: if name != user.name and \ not ircdb.checkCapability(msg.prefix, 'owner'): - irc.error('You may only retrieve your own hostmasks.', - Raise=True) + irc.error(_('You may only retrieve your own ' + '(hostmasks.'), Raise=True) else: try: user = ircdb.users.getUser(name) @@ -279,6 +294,7 @@ class User(callbacks.Plugin): irc.errorNotRegistered() list = wrap(list, ['private', additional('something')]) + @internationalizeDocstring def add(self, irc, msg, args, user, hostmask, password): """[] [] [] @@ -294,18 +310,18 @@ class User(callbacks.Plugin): if not hostmask: hostmask = msg.prefix if not ircutils.isUserHostmask(hostmask): - irc.errorInvalid('hostmask', hostmask, - 'Make sure your hostmask includes a nick, ' + irc.errorInvalid(_('hostmask'), hostmask, + _('Make sure your hostmask includes a nick, ' 'then an exclamation point (!), then a user, ' 'then an at symbol (@), then a host. Feel ' 'free to use wildcards (* and ?, which work ' 'just like they do on the command line) in ' - 'any of these parts.', + 'any of these parts.'), Raise=True) try: otherId = ircdb.users.getUserId(hostmask) if otherId != user.id: - irc.error('That hostmask is already registered.', + irc.error(_('That hostmask is already registered.'), Raise=True) except KeyError: pass @@ -328,11 +344,13 @@ class User(callbacks.Plugin): except ValueError, e: irc.error(str(e), Raise=True) except ircdb.DuplicateHostmask: - irc.error('That hostmask is already registered.', Raise=True) + irc.error(_('That hostmask is already registered.'), + Raise=True) irc.replySuccess() add = wrap(add, ['private', first('otherUser', 'user'), optional('something'), additional('something', '')]) + @internationalizeDocstring def remove(self, irc, msg, args, user, hostmask, password): """ [] @@ -353,17 +371,18 @@ class User(callbacks.Plugin): s = '' if hostmask == 'all': user.hostmasks.clear() - s = 'All hostmasks removed.' + s = _('All hostmasks removed.') else: user.removeHostmask(hostmask) except KeyError: - irc.error('There was no such hostmask.') + irc.error(_('There was no such hostmask.')) return ircdb.users.setUser(user) irc.replySuccess(s) remove = wrap(remove, ['private', 'otherUser', 'something', additional('something', '')]) + @internationalizeDocstring def capabilities(self, irc, msg, args, user): """[] @@ -383,6 +402,7 @@ class User(callbacks.Plugin): Raise=True) capabilities = wrap(capabilities, [first('otherUser', 'user')]) + @internationalizeDocstring def identify(self, irc, msg, args, user, password): """ @@ -396,14 +416,15 @@ class User(callbacks.Plugin): ircdb.users.setUser(user, flush=False) irc.replySuccess() except ValueError: - irc.error('Your secure flag is true and your hostmask ' - 'doesn\'t match any of your known hostmasks.') + irc.error(_('Your secure flag is true and your hostmask ' + 'doesn\'t match any of your known hostmasks.')) else: self.log.warning('Failed identification attempt by %s (password ' 'did not match for %s).', msg.prefix, user.name) irc.error(conf.supybot.replies.incorrectAuthentication()) identify = wrap(identify, ['private', 'otherUser', 'something']) + @internationalizeDocstring def unidentify(self, irc, msg, args, user): """takes no arguments @@ -421,6 +442,7 @@ class User(callbacks.Plugin): 'recognized.') unidentify = wrap(unidentify, ['user']) + @internationalizeDocstring def whoami(self, irc, msg, args): """takes no arguments @@ -430,9 +452,10 @@ class User(callbacks.Plugin): user = ircdb.users.getUser(msg.prefix) irc.reply(user.name) except KeyError: - irc.reply('I don\'t recognize you.') + irc.reply(_('I don\'t recognize you.')) whoami = wrap(whoami) + @internationalizeDocstring def stats(self, irc, msg, args): """takes no arguments @@ -452,9 +475,9 @@ class User(callbacks.Plugin): admins += 1 except KeyError: pass - irc.reply(format('I have %s registered users ' + irc.reply(format(_('I have %s registered users ' 'with %s registered hostmasks; ' - '%n and %n.', + '%n and %n.'), users, hostmasks, (owners, 'owner'), (admins, 'admin'))) stats = wrap(stats) From ca23f946e5f259c24b3a94a9ef7a2cd3bfdd2d0b Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sat, 16 Oct 2010 18:54:18 +0200 Subject: [PATCH 024/136] Internationalize Alias, Anonymous, AutoMode, and BadWord --- plugins/Alias/config.py | 2 + plugins/Alias/messages.pot | 87 ++++++++++++++++++++++++++ plugins/Alias/plugin.py | 16 +++-- plugins/Anonymous/config.py | 22 ++++--- plugins/Anonymous/messages.pot | 79 +++++++++++++++++++++++ plugins/Anonymous/plugin.py | 5 ++ plugins/AutoMode/config.py | 35 ++++++----- plugins/AutoMode/messages.pot | 69 +++++++++++++++++++++ plugins/AutoMode/plugin.py | 2 + plugins/BadWords/config.py | 41 ++++++------ plugins/BadWords/messages.pot | 110 +++++++++++++++++++++++++++++++++ plugins/BadWords/plugin.py | 7 ++- 12 files changed, 425 insertions(+), 50 deletions(-) create mode 100644 plugins/Alias/messages.pot create mode 100644 plugins/Anonymous/messages.pot create mode 100644 plugins/AutoMode/messages.pot create mode 100644 plugins/BadWords/messages.pot diff --git a/plugins/Alias/config.py b/plugins/Alias/config.py index c83c30a58..4d37ed6a5 100644 --- a/plugins/Alias/config.py +++ b/plugins/Alias/config.py @@ -29,6 +29,8 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Alias') def configure(advanced): # This will be called by supybot to configure this module. advanced is diff --git a/plugins/Alias/messages.pot b/plugins/Alias/messages.pot new file mode 100644 index 000000000..ab8d0ac06 --- /dev/null +++ b/plugins/Alias/messages.pot @@ -0,0 +1,87 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-16 14:10+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: plugin.py:45 +#, docstring +msgid "" +"Returns the channel the msg came over or the channel given in args.\n" +"\n" +" If the channel was given in args, args is modified (the channel is\n" +" removed).\n" +" " +msgstr "" + +#: plugin.py:164 +msgid " at least" +msgstr "" + +#: plugin.py:165 +msgid "" +"\n" +"\n" +"Alias for %q." +msgstr "" + +#: plugin.py:220 +#, docstring +msgid "" +"\n" +"\n" +" Locks an alias so that no one else can change it.\n" +" " +msgstr "" + +#: plugin.py:229 plugin.py:243 +msgid "There is no such alias." +msgstr "" + +#: plugin.py:234 +#, docstring +msgid "" +"\n" +"\n" +" Unlocks an alias so that people can define new aliases over it.\n" +" " +msgstr "" + +#: plugin.py:254 +msgid "That name isn't valid. Try %q instead." +msgstr "" + +#: plugin.py:292 +#, docstring +msgid "" +" \n" +"\n" +" Defines an alias that executes . The \n" +" should be in the standard \"command argument [nestedcommand argument]\"\n" +" arguments to the alias; they'll be filled with the first, second, etc.\n" +" arguments. $1, $2, etc. can be used for required arguments. @1, @2,\n" +" etc. can be used for optional arguments. $* simply means \"all\n" +" remaining arguments,\" and cannot be combined with optional arguments.\n" +" " +msgstr "" + +#: plugin.py:315 +#, docstring +msgid "" +"\n" +"\n" +" Removes the given alias, if unlocked.\n" +" " +msgstr "" + diff --git a/plugins/Alias/plugin.py b/plugins/Alias/plugin.py index f90eb29cf..d43f22b4f 100644 --- a/plugins/Alias/plugin.py +++ b/plugins/Alias/plugin.py @@ -37,6 +37,8 @@ from supybot.commands import * import supybot.ircutils as ircutils import supybot.registry as registry import supybot.callbacks as callbacks +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Alias') # Copied from the old privmsgs.py. def getChannel(msg, args=()): @@ -159,8 +161,8 @@ def makeNewAlias(name, alias): self.Proxy(irc, msg, tokens) flexargs = '' if biggestDollar and (wildcard or biggestAt): - flexargs = ' at least' - doc =format('\n\nAlias for %q.', + flexargs = _(' at least') + doc =format(_('\n\nAlias for %q.'), flexargs, (biggestDollar, 'argument'), alias) f = utils.python.changeFunctionName(f, name, doc) return f @@ -213,6 +215,7 @@ class Alias(callbacks.Plugin): except AttributeError: return self.aliases[command[0]][2] + @internationalizeDocstring def lock(self, irc, msg, args, name): """ @@ -223,9 +226,10 @@ class Alias(callbacks.Plugin): conf.supybot.plugins.Alias.aliases.get(name).locked.setValue(True) irc.replySuccess() else: - irc.error('There is no such alias.') + irc.error(_('There is no such alias.')) lock = wrap(lock, [('checkCapability', 'admin'), 'commandName']) + @internationalizeDocstring def unlock(self, irc, msg, args, name): """ @@ -236,7 +240,7 @@ class Alias(callbacks.Plugin): conf.supybot.plugins.Alias.aliases.get(name).locked.setValue(False) irc.replySuccess() else: - irc.error('There is no such alias.') + irc.error(_('There is no such alias.')) unlock = wrap(unlock, [('checkCapability', 'admin'), 'commandName']) _invalidCharsRe = re.compile(r'[\[\]\s]') @@ -247,7 +251,7 @@ class Alias(callbacks.Plugin): raise AliasError, 'Names cannot contain pipes.' realName = callbacks.canonicalName(name) if name != realName: - s = format('That name isn\'t valid. Try %q instead.', realName) + s = format(_('That name isn\'t valid. Try %q instead.'), realName) raise AliasError, s name = realName if self.isCommandMethod(name): @@ -283,6 +287,7 @@ class Alias(callbacks.Plugin): else: raise AliasError, 'There is no such alias.' + @internationalizeDocstring def add(self, irc, msg, args, name, alias): """ @@ -305,6 +310,7 @@ class Alias(callbacks.Plugin): irc.error(str(e)) add = wrap(add, ['commandName', 'text']) + @internationalizeDocstring def remove(self, irc, msg, args, name): """ diff --git a/plugins/Anonymous/config.py b/plugins/Anonymous/config.py index 6fbd75829..51e6ba70b 100644 --- a/plugins/Anonymous/config.py +++ b/plugins/Anonymous/config.py @@ -29,6 +29,8 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Anonymous') def configure(advanced): # This will be called by supybot to configure this module. advanced is @@ -44,20 +46,20 @@ Anonymous = conf.registerPlugin('Anonymous') # conf.registerGlobalValue(Anonymous, 'someConfigVariableName', # registry.Boolean(False, """Help for someConfigVariableName.""")) conf.registerChannelValue(conf.supybot.plugins.Anonymous, - 'requirePresenceInChannel', registry.Boolean(True, """Determines whether + 'requirePresenceInChannel', registry.Boolean(True, _("""Determines whether the bot should require people trying to use this plugin to be in the - channel they wish to anonymously send to.""")) + channel they wish to anonymously send to."""))) conf.registerGlobalValue(conf.supybot.plugins.Anonymous, 'requireRegistration', - registry.Boolean(True, """Determines whether the bot should require people - trying to use this plugin to be registered.""")) + registry.Boolean(True, _("""Determines whether the bot should require + people trying to use this plugin to be registered."""))) conf.registerGlobalValue(conf.supybot.plugins.Anonymous, 'requireCapability', - registry.String('', """Determines what capability (if any) the bot should - require people trying to use this plugin to have.""")) + registry.String('', _("""Determines what capability (if any) the bot should + require people trying to use this plugin to have."""))) conf.registerGlobalValue(conf.supybot.plugins.Anonymous, 'allowPrivateTarget', - registry.Boolean(False, """Determines whether the bot will require targets - of the "say" command to be public (i.e., channels). If this is True, the - bot will allow people to use the "say" command to send private messages to - other users.""")) + registry.Boolean(False, _("""Determines whether the bot will require + targets of the "say" command to be public (i.e., channels). If this is + True, the bot will allow people to use the "say" command to send private + messages to other users."""))) # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/Anonymous/messages.pot b/plugins/Anonymous/messages.pot new file mode 100644 index 000000000..53bc52bb4 --- /dev/null +++ b/plugins/Anonymous/messages.pot @@ -0,0 +1,79 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-16 15:14+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: config.py:49 +msgid "" +"Determines whether\n" +" the bot should require people trying to use this plugin to be in the\n" +" channel they wish to anonymously send to." +msgstr "" + +#: config.py:53 +msgid "" +"Determines whether the bot should require\n" +" people trying to use this plugin to be registered." +msgstr "" + +#: config.py:56 +msgid "" +"Determines what capability (if any) the bot should\n" +" require people trying to use this plugin to have." +msgstr "" + +#: config.py:59 +msgid "" +"Determines whether the bot will require \n" +" targets of the \"say\" command to be public (i.e., channels). If this is\n" +" True, the bot will allow people to use the \"say\" command to send private\n" +" messages to other users." +msgstr "" + +#: plugin.py:41 +#, docstring +msgid "" +"This plugin allows users to act through the bot anonymously. The 'do'\n" +" command has the bot perform an anonymous action in a given channel, and\n" +" the 'say' command allows other people to speak through the bot. Since\n" +" this can be fairly well abused, you might want to set\n" +" supybot.plugins.Anonymous.requireCapability so only users with that\n" +" capability can use this plugin. For extra security, you can require that\n" +" the user be *in* the channel they are trying to address anonymously with\n" +" supybot.plugins.Anonymous.requirePresenceInChannel, or you can require\n" +" that the user be registered by setting\n" +" supybot.plugins.Anonymous.requireRegistration.\n" +" " +msgstr "" + +#: plugin.py:81 +#, docstring +msgid "" +" \n" +"\n" +" Sends to . Can only send to if\n" +" supybot.plugins.Anonymous.allowPrivateTarget is True.\n" +" " +msgstr "" + +#: plugin.py:95 +#, docstring +msgid "" +" \n" +"\n" +" Performs in .\n" +" " +msgstr "" + diff --git a/plugins/Anonymous/plugin.py b/plugins/Anonymous/plugin.py index 9eb95260d..4588ee451 100644 --- a/plugins/Anonymous/plugin.py +++ b/plugins/Anonymous/plugin.py @@ -33,7 +33,10 @@ import supybot.utils as utils from supybot.commands import * import supybot.ircmsgs as ircmsgs import supybot.callbacks as callbacks +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Anonymous') +@internationalizeDocstring class Anonymous(callbacks.Plugin): """This plugin allows users to act through the bot anonymously. The 'do' command has the bot perform an anonymous action in a given channel, and @@ -73,6 +76,7 @@ class Anonymous(callbacks.Plugin): action), Raise=True) + @internationalizeDocstring def say(self, irc, msg, args, target, text): """ @@ -86,6 +90,7 @@ class Anonymous(callbacks.Plugin): irc.noReply() say = wrap(say, [first('nick', 'inChannel'), 'text']) + @internationalizeDocstring def do(self, irc, msg, args, channel, text): """ diff --git a/plugins/AutoMode/config.py b/plugins/AutoMode/config.py index 98f61349f..052bed457 100644 --- a/plugins/AutoMode/config.py +++ b/plugins/AutoMode/config.py @@ -29,6 +29,8 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('AutoMode') def configure(advanced): # This will be called by supybot to configure this module. advanced is @@ -41,30 +43,33 @@ def configure(advanced): AutoMode = conf.registerPlugin('AutoMode') conf.registerChannelValue(AutoMode, 'enable', - registry.Boolean(True, """Determines whether this plugin is enabled.""")) + registry.Boolean(True, _("""Determines whether this plugin is enabled. + """))) conf.registerGlobalValue(AutoMode, 'owner', - registry.Boolean(True, """Determines whether this plugin will automode owners.""")) + registry.Boolean(True, _("""Determines whether this plugin will automode + owners."""))) conf.registerChannelValue(AutoMode, 'fallthrough', - registry.Boolean(False, """Determines whether the bot will "fall through" to - halfop/voicing when auto-opping is turned off but auto-halfopping/voicing - are turned on.""")) + registry.Boolean(False, _("""Determines whether the bot will "fall through + to halfop/voicing when auto-opping is turned off but + auto-halfopping/voicing are turned on."""))) conf.registerChannelValue(AutoMode, 'op', - registry.Boolean(True, """Determines whether the bot will automatically op - people with the ,op capability when they join the channel.""")) + registry.Boolean(True, _("""Determines whether the bot will automatically + op people with the ,op capability when they join the channel. + """))) conf.registerChannelValue(AutoMode, 'halfop', - registry.Boolean(True, """Determines whether the bot will automatically + registry.Boolean(True, _("""Determines whether the bot will automatically halfop people with the ,halfop capability when they join the - channel.""")) + channel."""))) conf.registerChannelValue(AutoMode, 'voice', - registry.Boolean(True, """Determines whether the bot will automatically + registry.Boolean(True, _("""Determines whether the bot will automatically voice people with the ,voice capability when they join the - channel.""")) + channel."""))) conf.registerChannelValue(AutoMode, 'ban', - registry.Boolean(True, """Determines whether the bot will automatically ban - people who join the channel and are on the banlist.""")) + registry.Boolean(True, _("""Determines whether the bot will automatically + ban people who join the channel and are on the banlist."""))) conf.registerChannelValue(AutoMode.ban, 'period', - registry.PositiveInteger(86400, """Determines how many seconds the bot will - automatically ban a person when banning.""")) + registry.PositiveInteger(86400, _("""Determines how many seconds the bot + will automatically ban a person when banning."""))) diff --git a/plugins/AutoMode/messages.pot b/plugins/AutoMode/messages.pot new file mode 100644 index 000000000..d1330c69b --- /dev/null +++ b/plugins/AutoMode/messages.pot @@ -0,0 +1,69 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-16 18:48+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: config.py:46 +msgid "" +"Determines whether this plugin is enabled.\n" +" " +msgstr "" + +#: config.py:49 +msgid "" +"Determines whether this plugin will automode\n" +" owners." +msgstr "" + +#: config.py:52 +msgid "" +"Determines whether the bot will \"fall through\n" +" to halfop/voicing when auto-opping is turned off but\n" +" auto-halfopping/voicing are turned on." +msgstr "" + +#: config.py:56 +msgid "" +"Determines whether the bot will automatically\n" +" op people with the ,op capability when they join the channel.\n" +" " +msgstr "" + +#: config.py:60 +msgid "" +"Determines whether the bot will automatically\n" +" halfop people with the ,halfop capability when they join the\n" +" channel." +msgstr "" + +#: config.py:64 +msgid "" +"Determines whether the bot will automatically\n" +" voice people with the ,voice capability when they join the\n" +" channel." +msgstr "" + +#: config.py:68 +msgid "" +"Determines whether the bot will automatically\n" +" ban people who join the channel and are on the banlist." +msgstr "" + +#: config.py:71 +msgid "" +"Determines how many seconds the bot\n" +" will automatically ban a person when banning." +msgstr "" + diff --git a/plugins/AutoMode/plugin.py b/plugins/AutoMode/plugin.py index 8c7fd1a69..73f1a64bb 100644 --- a/plugins/AutoMode/plugin.py +++ b/plugins/AutoMode/plugin.py @@ -35,6 +35,8 @@ import supybot.ircmsgs as ircmsgs import supybot.ircutils as ircutils import supybot.schedule as schedule import supybot.callbacks as callbacks +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('AutoMode') class Continue(Exception): pass # Used below, look in the "do" function nested in doJoin. diff --git a/plugins/BadWords/config.py b/plugins/BadWords/config.py index d88d0a5f4..a2b130a4a 100644 --- a/plugins/BadWords/config.py +++ b/plugins/BadWords/config.py @@ -31,12 +31,15 @@ import time import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('BadWords') def configure(advanced): from supybot.questions import output, expect, anything, something, yn conf.registerPlugin('BadWords', True) - if yn('Would you like to add some bad words?'): - words = anything('What words? (separate individual words by spaces)') + if yn(_('Would you like to add some bad words?')): + words = anything(_('What words? (separate individual words by ' + 'spaces)')) conf.supybot.plugins.BadWords.words.set(words) class LastModifiedSetOfStrings(registry.SpaceSeparatedSetOfStrings): @@ -47,14 +50,14 @@ class LastModifiedSetOfStrings(registry.SpaceSeparatedSetOfStrings): BadWords = conf.registerPlugin('BadWords') conf.registerGlobalValue(BadWords, 'words', - LastModifiedSetOfStrings([], """Determines what words are - considered to be 'bad' so the bot won't say them.""")) + LastModifiedSetOfStrings([], _("""Determines what words are + considered to be 'bad' so the bot won't say them."""))) conf.registerGlobalValue(BadWords,'requireWordBoundaries', - registry.Boolean(False, """Determines whether the bot will require bad + registry.Boolean(False, _("""Determines whether the bot will require bad words to be independent words, or whether it will censor them within other words. For instance, if 'darn' is a bad word, then if this is true, 'darn' will be censored, but 'darnit' will not. You probably want this to be - false.""")) + false."""))) class String256(registry.String): def __call__(self): @@ -65,39 +68,39 @@ class String256(registry.String): return self.value conf.registerGlobalValue(BadWords, 'nastyChars', - String256('!@#&', """Determines what characters will replace bad words; a + String256('!@#&', _("""Determines what characters will replace bad words; a chunk of these characters matching the size of the replaced bad word will - be used to replace the bad words you've configured.""")) + be used to replace the bad words you've configured."""))) class ReplacementMethods(registry.OnlySomeStrings): validStrings = ('simple', 'nastyCharacters') conf.registerGlobalValue(BadWords, 'replaceMethod', - ReplacementMethods('nastyCharacters', """Determines the manner in which + ReplacementMethods('nastyCharacters', _("""Determines the manner in which bad words will be replaced. 'nastyCharacters' (the default) will replace a bad word with the same number of 'nasty characters' (like those used in comic books; configurable by supybot.plugins.BadWords.nastyChars). 'simple' will replace a bad word with a simple strings (regardless of the length of the bad word); this string is configurable via - supybot.plugins.BadWords.simpleReplacement.""")) + supybot.plugins.BadWords.simpleReplacement."""))) conf.registerGlobalValue(BadWords,'simpleReplacement', - registry.String('[CENSORED]', """Determines what word will replace bad - words if the replacement method is 'simple'.""")) + registry.String('[CENSORED]', _("""Determines what word will replace bad + words if the replacement method is 'simple'."""))) conf.registerGlobalValue(BadWords, 'stripFormatting', - registry.Boolean(True, """Determines whether the bot will strip + registry.Boolean(True, _("""Determines whether the bot will strip formatting characters from messages before it checks them for bad words. If this is False, it will be relatively trivial to circumvent this plugin's filtering. If it's True, however, it will interact poorly with other - plugins that do coloring or bolding of text.""")) + plugins that do coloring or bolding of text."""))) conf.registerChannelValue(BadWords, 'kick', - registry.Boolean(False, """Determines whether the bot will kick people with - a warning when they use bad words.""")) + registry.Boolean(False, _("""Determines whether the bot will kick people with + a warning when they use bad words."""))) conf.registerChannelValue(BadWords.kick, 'message', - registry.NormalizedString("""You have been kicked for using a word + registry.NormalizedString(_("""You have been kicked for using a word prohibited in the presence of this bot. Please use more appropriate - language in the future.""", """Determines the kick message used by the bot - when kicking users for saying bad words.""")) + language in the future."""), _("""Determines the kick message used by the + bot when kicking users for saying bad words."""))) # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/BadWords/messages.pot b/plugins/BadWords/messages.pot new file mode 100644 index 000000000..8363e3e24 --- /dev/null +++ b/plugins/BadWords/messages.pot @@ -0,0 +1,110 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-16 18:51+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: config.py:40 +msgid "Would you like to add some bad words?" +msgstr "" + +#: config.py:41 +msgid "What words? (separate individual words by spaces)" +msgstr "" + +#: config.py:53 +msgid "" +"Determines what words are\n" +" considered to be 'bad' so the bot won't say them." +msgstr "" + +#: config.py:56 +msgid "" +"Determines whether the bot will require bad\n" +" words to be independent words, or whether it will censor them within other\n" +" words. For instance, if 'darn' is a bad word, then if this is true, 'darn'\n" +" will be censored, but 'darnit' will not. You probably want this to be\n" +" false." +msgstr "" + +#: config.py:71 +msgid "" +"Determines what characters will replace bad words; a\n" +" chunk of these characters matching the size of the replaced bad word will\n" +" be used to replace the bad words you've configured." +msgstr "" + +#: config.py:79 +msgid "" +"Determines the manner in which\n" +" bad words will be replaced. 'nastyCharacters' (the default) will replace a\n" +" bad word with the same number of 'nasty characters' (like those used in\n" +" comic books; configurable by supybot.plugins.BadWords.nastyChars).\n" +" 'simple' will replace a bad word with a simple strings (regardless of the\n" +" length of the bad word); this string is configurable via\n" +" supybot.plugins.BadWords.simpleReplacement." +msgstr "" + +#: config.py:87 +msgid "" +"Determines what word will replace bad\n" +" words if the replacement method is 'simple'." +msgstr "" + +#: config.py:90 +msgid "" +"Determines whether the bot will strip\n" +" formatting characters from messages before it checks them for bad words.\n" +" If this is False, it will be relatively trivial to circumvent this plugin's\n" +" filtering. If it's True, however, it will interact poorly with other\n" +" plugins that do coloring or bolding of text." +msgstr "" + +#: config.py:97 +msgid "" +"Determines whether the bot will kick people with\n" +" a warning when they use bad words." +msgstr "" + +#: plugin.py:110 +#, docstring +msgid "" +"takes no arguments\n" +"\n" +" Returns the list of words being censored.\n" +" " +msgstr "" + +#: plugin.py:120 +msgid "I'm not currently censoring any bad words." +msgstr "" + +#: plugin.py:125 +#, docstring +msgid "" +" [ ...]\n" +"\n" +" Adds all s to the list of words the bot isn't to say.\n" +" " +msgstr "" + +#: plugin.py:137 +#, docstring +msgid "" +" [ ...]\n" +"\n" +" Removes a s from the list of words the bot isn't to say.\n" +" " +msgstr "" + diff --git a/plugins/BadWords/plugin.py b/plugins/BadWords/plugin.py index e373567a4..5ea306b00 100644 --- a/plugins/BadWords/plugin.py +++ b/plugins/BadWords/plugin.py @@ -39,6 +39,8 @@ import supybot.ircmsgs as ircmsgs from supybot.commands import * import supybot.ircutils as ircutils import supybot.callbacks as callbacks +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('BadWords') class BadWords(callbacks.Privmsg): def __init__(self, irc): @@ -103,6 +105,7 @@ class BadWords(callbacks.Privmsg): s = r'\b%s\b' % s self.regexp = re.compile(s, re.I) + @internationalizeDocstring def list(self, irc, msg, args): """takes no arguments @@ -114,9 +117,10 @@ class BadWords(callbacks.Privmsg): utils.sortBy(str.lower, L) irc.reply(format('%L', L)) else: - irc.reply('I\'m not currently censoring any bad words.') + irc.reply(_('I\'m not currently censoring any bad words.')) list = wrap(list, ['admin']) + @internationalizeDocstring def add(self, irc, msg, args, words): """ [ ...] @@ -128,6 +132,7 @@ class BadWords(callbacks.Privmsg): irc.replySuccess() add = wrap(add, ['admin', many('something')]) + @internationalizeDocstring def remove(self, irc, msg, args, words): """ [ ...] From d14bb0cc166aebedb67bc218e6cf171347733d26 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sun, 17 Oct 2010 11:22:07 +0200 Subject: [PATCH 025/136] Internationalize ChannelLogger, Ctcp, Dict, Dunno, Factoids --- plugins/ChannelLogger/config.py | 48 +++---- plugins/ChannelLogger/messages.pot | 97 ++++++++++++++ plugins/ChannelLogger/plugin.py | 2 + plugins/Ctcp/config.py | 2 + plugins/Ctcp/messages.pot | 64 ++++++++++ plugins/Ctcp/plugin.py | 5 +- plugins/Dict/config.py | 14 +- plugins/Dict/messages.pot | 82 ++++++++++++ plugins/Dict/plugin.py | 16 ++- plugins/Dunno/config.py | 6 +- plugins/Dunno/messages.pot | 33 +++++ plugins/Dunno/plugin.py | 3 + plugins/Factoids/config.py | 18 +-- plugins/Factoids/messages.pot | 198 +++++++++++++++++++++++++++++ plugins/Factoids/plugin.py | 46 ++++--- 15 files changed, 571 insertions(+), 63 deletions(-) create mode 100644 plugins/ChannelLogger/messages.pot create mode 100644 plugins/Ctcp/messages.pot create mode 100644 plugins/Dict/messages.pot create mode 100644 plugins/Dunno/messages.pot create mode 100644 plugins/Factoids/messages.pot diff --git a/plugins/ChannelLogger/config.py b/plugins/ChannelLogger/config.py index eae2ac7a4..f9935e4f4 100644 --- a/plugins/ChannelLogger/config.py +++ b/plugins/ChannelLogger/config.py @@ -30,6 +30,8 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('ChannelLogger') def configure(advanced): # This will be called by supybot to configure this module. advanced is @@ -41,50 +43,50 @@ def configure(advanced): ChannelLogger = conf.registerPlugin('ChannelLogger') conf.registerChannelValue(ChannelLogger, 'enable', - registry.Boolean(True, """Determines whether logging is enabled.""")) + registry.Boolean(True, _("""Determines whether logging is enabled."""))) conf.registerGlobalValue(ChannelLogger, 'flushImmediately', - registry.Boolean(False, """Determines whether channel logfiles will be + registry.Boolean(False, _("""Determines whether channel logfiles will be flushed anytime they're written to, rather than being buffered by the - operating system.""")) + operating system."""))) conf.registerChannelValue(ChannelLogger, 'stripFormatting', - registry.Boolean(True, """Determines whether formatting characters (such - as bolding, color, etc.) are removed when writing the logs to disk.""")) + registry.Boolean(True, _("""Determines whether formatting characters (such + as bolding, color, etc.) are removed when writing the logs to disk."""))) conf.registerChannelValue(ChannelLogger, 'timestamp', - registry.Boolean(True, """Determines whether the logs for this channel are - timestamped with the timestamp in supybot.log.timestampFormat.""")) + registry.Boolean(True, _("""Determines whether the logs for this channel are + timestamped with the timestamp in supybot.log.timestampFormat."""))) conf.registerChannelValue(ChannelLogger, 'noLogPrefix', - registry.String('[nolog]', """Determines what string a message should be + registry.String('[nolog]', _("""Determines what string a message should be prefixed with in order not to be logged. If you don't want any such - prefix, just set it to the empty string.""")) + prefix, just set it to the empty string."""))) conf.registerChannelValue(ChannelLogger, 'rotateLogs', - registry.Boolean(False, """Determines whether the bot will automatically + registry.Boolean(False, _("""Determines whether the bot will automatically rotate the logs for this channel. The bot will rotate logs when the timestamp for the log changes. The timestamp is set according to - the 'filenameTimestamp' configuration variable.""")) + the 'filenameTimestamp' configuration variable."""))) conf.registerChannelValue(ChannelLogger, 'filenameTimestamp', - registry.String('%Y-%m-%d', """Determines how to represent the timestamp + registry.String('%Y-%m-%d', _("""Determines how to represent the timestamp used for the filename in rotated logs. When this timestamp changes, the old logfiles will be closed and a new one started. The format characters for the timestamp are in the time.strftime docs at python.org. In order for your logs to be rotated, you'll also have to enable - supybot.plugins.ChannelLogger.rotateLogs.""")) + supybot.plugins.ChannelLogger.rotateLogs."""))) conf.registerGlobalValue(ChannelLogger, 'directories', - registry.Boolean(True, """Determines whether the bot will partition its - channel logs into separate directories based on different criteria.""")) + registry.Boolean(True, _("""Determines whether the bot will partition its + channel logs into separate directories based on different criteria."""))) conf.registerGlobalValue(ChannelLogger.directories, 'network', - registry.Boolean(True, """Determines whether the bot will use a network - directory if using directories.""")) + registry.Boolean(True, _("""Determines whether the bot will use a network + directory if using directories."""))) conf.registerGlobalValue(ChannelLogger.directories, 'channel', - registry.Boolean(True, """Determines whether the bot will use a channel - directory if using directories.""")) + registry.Boolean(True, _("""Determines whether the bot will use a channel + directory if using directories."""))) conf.registerGlobalValue(ChannelLogger.directories, 'timestamp', - registry.Boolean(False, """Determines whether the bot will use a timestamp + registry.Boolean(False, _("""Determines whether the bot will use a timestamp (determined by supybot.plugins.ChannelLogger.directories.timestamp.format) - if using directories.""")) + if using directories."""))) conf.registerGlobalValue(ChannelLogger.directories.timestamp, 'format', - registry.String('%B', """Determines what timestamp format will be used in + registry.String('%B', _("""Determines what timestamp format will be used in the directory stucture for channel logs if - supybot.plugins.ChannelLogger.directories.timestamp is True.""")) + supybot.plugins.ChannelLogger.directories.timestamp is True."""))) # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/ChannelLogger/messages.pot b/plugins/ChannelLogger/messages.pot new file mode 100644 index 000000000..57b359a3c --- /dev/null +++ b/plugins/ChannelLogger/messages.pot @@ -0,0 +1,97 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-17 10:02+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: config.py:46 +msgid "Determines whether logging is enabled." +msgstr "" + +#: config.py:48 +msgid "" +"Determines whether channel logfiles will be\n" +" flushed anytime they're written to, rather than being buffered by the\n" +" operating system." +msgstr "" + +#: config.py:52 +msgid "" +"Determines whether formatting characters (such\n" +" as bolding, color, etc.) are removed when writing the logs to disk." +msgstr "" + +#: config.py:55 +msgid "" +"Determines whether the logs for this channel are\n" +" timestamped with the timestamp in supybot.log.timestampFormat." +msgstr "" + +#: config.py:58 +msgid "" +"Determines what string a message should be\n" +" prefixed with in order not to be logged. If you don't want any such\n" +" prefix, just set it to the empty string." +msgstr "" + +#: config.py:62 +msgid "" +"Determines whether the bot will automatically\n" +" rotate the logs for this channel. The bot will rotate logs when the\n" +" timestamp for the log changes. The timestamp is set according to\n" +" the 'filenameTimestamp' configuration variable." +msgstr "" + +#: config.py:67 +msgid "" +"Determines how to represent the timestamp\n" +" used for the filename in rotated logs. When this timestamp changes, the\n" +" old logfiles will be closed and a new one started. The format characters\n" +" for the timestamp are in the time.strftime docs at python.org. In order\n" +" for your logs to be rotated, you'll also have to enable\n" +" supybot.plugins.ChannelLogger.rotateLogs." +msgstr "" + +#: config.py:75 +msgid "" +"Determines whether the bot will partition its\n" +" channel logs into separate directories based on different criteria." +msgstr "" + +#: config.py:78 +msgid "" +"Determines whether the bot will use a network\n" +" directory if using directories." +msgstr "" + +#: config.py:81 +msgid "" +"Determines whether the bot will use a channel\n" +" directory if using directories." +msgstr "" + +#: config.py:84 +msgid "" +"Determines whether the bot will use a timestamp\n" +" (determined by supybot.plugins.ChannelLogger.directories.timestamp.format)\n" +" if using directories." +msgstr "" + +#: config.py:88 +msgid "" +"Determines what timestamp format will be used in\n" +" the directory stucture for channel logs if\n" +" supybot.plugins.ChannelLogger.directories.timestamp is True." +msgstr "" + diff --git a/plugins/ChannelLogger/plugin.py b/plugins/ChannelLogger/plugin.py index 93f9b3c06..6296cccbc 100644 --- a/plugins/ChannelLogger/plugin.py +++ b/plugins/ChannelLogger/plugin.py @@ -39,6 +39,8 @@ import supybot.ircmsgs as ircmsgs import supybot.ircutils as ircutils import supybot.registry as registry import supybot.callbacks as callbacks +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('ChannelLogger') class FakeLog(object): def flush(self): diff --git a/plugins/Ctcp/config.py b/plugins/Ctcp/config.py index 3f1f49dc0..8e19f3273 100644 --- a/plugins/Ctcp/config.py +++ b/plugins/Ctcp/config.py @@ -29,6 +29,8 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Ctcp') def configure(advanced): # This will be called by supybot to configure this module. advanced is diff --git a/plugins/Ctcp/messages.pot b/plugins/Ctcp/messages.pot new file mode 100644 index 000000000..82cf6a28c --- /dev/null +++ b/plugins/Ctcp/messages.pot @@ -0,0 +1,64 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-17 10:16+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: plugin.py:77 +#, docstring +msgid "\001PING ?(.*)\001" +msgstr "" + +#: plugin.py:86 +#, docstring +msgid "\001VERSION\001" +msgstr "" + +#: plugin.py:91 +#, docstring +msgid "\001USERINFO\001" +msgstr "" + +#: plugin.py:96 +#, docstring +msgid "\001TIME\001" +msgstr "" + +#: plugin.py:101 +#, docstring +msgid "\001FINGER\001" +msgstr "" + +#: plugin.py:104 +msgid "FINGER Supybot, the best Python IRC bot in existence!" +msgstr "" + +#: plugin.py:107 +#, docstring +msgid "\001SOURCE\001" +msgstr "" + +#: plugin.py:123 +#, docstring +msgid "" +"[] [--nicks]\n" +"\n" +" Sends a CTCP VERSION to , returning the various\n" +" version strings returned. It waits for 10 seconds before returning\n" +" the versions received at that point. If --nicks is given, nicks are\n" +" associated with the version strings; otherwise, only the version\n" +" strings are given.\n" +" " +msgstr "" + diff --git a/plugins/Ctcp/plugin.py b/plugins/Ctcp/plugin.py index e62404083..fb9b89b03 100644 --- a/plugins/Ctcp/plugin.py +++ b/plugins/Ctcp/plugin.py @@ -36,6 +36,8 @@ import supybot.ircmsgs as ircmsgs import supybot.ircutils as ircutils import supybot.schedule as schedule import supybot.callbacks as callbacks +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Ctcp') class Ctcp(callbacks.PluginRegexp): public = False @@ -99,7 +101,7 @@ class Ctcp(callbacks.PluginRegexp): "\x01FINGER\x01" self.log.info('Received CTCP FINGER from %s', msg.prefix) self._reply(irc, msg, - 'FINGER Supybot, the best Python IRC bot in existence!') + _('FINGER Supybot, the best Python IRC bot in existence!')) def ctcpSource(self, irc, msg, match): "\x01SOURCE\x01" @@ -116,6 +118,7 @@ class Ctcp(callbacks.PluginRegexp): if version == 'VERSION': self.versions.setdefault(payload, []).append(msg.nick) + @internationalizeDocstring def version(self, irc, msg, args, channel, optlist): """[] [--nicks] diff --git a/plugins/Dict/config.py b/plugins/Dict/config.py index d69bd0f28..4ac2452d3 100644 --- a/plugins/Dict/config.py +++ b/plugins/Dict/config.py @@ -29,22 +29,24 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Dict') def configure(advanced): from supybot.questions import output, expect, anything, something, yn conf.registerPlugin('Dict', True) - output('The default dictd server is dict.org.') - if yn('Would you like to specify a different dictd server?'): + output(_('The default dictd server is dict.org.')) + if yn(_('Would you like to specify a different dictd server?')): server = something('What server?') conf.supybot.plugins.Dict.server.set(server) Dict = conf.registerPlugin('Dict') conf.registerGlobalValue(Dict, 'server', - registry.String('dict.org', """Determines what server the bot will - retrieve definitions from.""")) + registry.String('dict.org', _("""Determines what server the bot will + retrieve definitions from."""))) conf.registerChannelValue(Dict, 'default', - registry.String('', """Determines the default dictionary the bot will + registry.String('', _("""Determines the default dictionary the bot will ask for definitions in. If this value is '*' (without the quotes) the bot - will use all dictionaries to define words.""")) + will use all dictionaries to define words."""))) # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/Dict/messages.pot b/plugins/Dict/messages.pot new file mode 100644 index 000000000..86e447f6f --- /dev/null +++ b/plugins/Dict/messages.pot @@ -0,0 +1,82 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-17 10:39+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: config.py:38 +msgid "The default dictd server is dict.org." +msgstr "" + +#: config.py:39 +msgid "Would you like to specify a different dictd server?" +msgstr "" + +#: config.py:45 +msgid "" +"Determines what server the bot will\n" +" retrieve definitions from." +msgstr "" + +#: config.py:48 +msgid "" +"Determines the default dictionary the bot will\n" +" ask for definitions in. If this value is '*' (without the quotes) the bot\n" +" will use all dictionaries to define words." +msgstr "" + +#: plugin.py:52 +#, docstring +msgid "" +"takes no arguments\n" +"\n" +" Returns the dictionaries valid for the dict command.\n" +" " +msgstr "" + +#: plugin.py:68 +#, docstring +msgid "" +"takes no arguments\n" +"\n" +" Returns a random valid dictionary.\n" +" " +msgstr "" + +#: plugin.py:83 +#, docstring +msgid "" +"[] \n" +"\n" +" Looks up the definition of on the dictd server specified by\n" +" the supybot.plugins.Dict.server config variable.\n" +" " +msgstr "" + +#: plugin.py:106 +msgid "You must give a word to define." +msgstr "" + +#: plugin.py:112 +msgid "No definition for %q could be found." +msgstr "" + +#: plugin.py:115 +msgid "No definition for %q could be found in %s" +msgstr "" + +#: plugin.py:127 +msgid "%L responded: %s" +msgstr "" + diff --git a/plugins/Dict/plugin.py b/plugins/Dict/plugin.py index 989ee687b..080edd2d5 100644 --- a/plugins/Dict/plugin.py +++ b/plugins/Dict/plugin.py @@ -35,6 +35,8 @@ import supybot.utils as utils from supybot.commands import * import supybot.ircutils as ircutils import supybot.callbacks as callbacks +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Dict') try: dictclient = utils.python.universalImport('dictclient', 'local.dictclient') @@ -45,6 +47,7 @@ except ImportError: class Dict(callbacks.Plugin): threaded = True + @internationalizeDocstring def dictionaries(self, irc, msg, args): """takes no arguments @@ -60,6 +63,7 @@ class Dict(callbacks.Plugin): irc.error(utils.web.strError(e)) dictionaries = wrap(dictionaries) + @internationalizeDocstring def random(self, irc, msg, args): """takes no arguments @@ -74,6 +78,7 @@ class Dict(callbacks.Plugin): irc.error(utils.web.strError(e)) random = wrap(random) + @internationalizeDocstring def dict(self, irc, msg, args, words): """[] @@ -98,16 +103,17 @@ class Dict(callbacks.Plugin): 'dictionary: %s.', msg.args[0], default) dictionary = '*' if not words: - irc.error('You must give a word to define.', Raise=True) + irc.error(_('You must give a word to define.'), Raise=True) word = ' '.join(words) definitions = conn.define(dictionary, word) dbs = set() if not definitions: if dictionary == '*': - irc.reply(format('No definition for %q could be found.', word)) + irc.reply(format(_('No definition for %q could be found.'), + word)) else: - irc.reply(format('No definition for %q could be found in %s', - word, ircutils.bold(dictionary))) + irc.reply(format(_('No definition for %q could be found in ' + '%s'), word, ircutils.bold(dictionary))) return L = [] for d in definitions: @@ -118,7 +124,7 @@ class Dict(callbacks.Plugin): L.append('%s: %s' % (db, s)) utils.sortBy(len, L) if dictionary == '*' and len(dbs) > 1: - s = format('%L responded: %s', list(dbs), '; '.join(L)) + s = format(_('%L responded: %s'), list(dbs), '; '.join(L)) else: s = '; '.join(L) irc.reply(s) diff --git a/plugins/Dunno/config.py b/plugins/Dunno/config.py index 8288c806b..819d888ce 100644 --- a/plugins/Dunno/config.py +++ b/plugins/Dunno/config.py @@ -29,6 +29,8 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Dunno') def configure(advanced): # This will be called by supybot to configure this module. advanced is @@ -41,8 +43,8 @@ def configure(advanced): Dunno = conf.registerPlugin('Dunno') conf.registerChannelValue(Dunno, 'prefixNick', - registry.Boolean(True, """Determines whether the bot will prefix the nick - of the user giving an invalid command to the "dunno" response.""")) + registry.Boolean(True, _("""Determines whether the bot will prefix the nick + of the user giving an invalid command to the "dunno" response."""))) # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/Dunno/messages.pot b/plugins/Dunno/messages.pot new file mode 100644 index 000000000..96ba98add --- /dev/null +++ b/plugins/Dunno/messages.pot @@ -0,0 +1,33 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-17 10:48+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: config.py:46 +msgid "" +"Determines whether the bot will prefix the nick\n" +" of the user giving an invalid command to the \"dunno\" response." +msgstr "" + +#: plugin.py:38 +#, docstring +msgid "" +"This plugin was written initially to work with MoobotFactoids, the two\n" +" of them to provide a similar-to-moobot-and-blootbot interface for factoids.\n" +" Basically, it replaces the standard 'Error: is not a valid command.'\n" +" messages with messages kept in a database, able to give more personable\n" +" responses." +msgstr "" + diff --git a/plugins/Dunno/plugin.py b/plugins/Dunno/plugin.py index 4f2f040de..189f14144 100644 --- a/plugins/Dunno/plugin.py +++ b/plugins/Dunno/plugin.py @@ -30,7 +30,10 @@ from supybot.commands import * import supybot.plugins as plugins import supybot.ircutils as ircutils +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Dunno') +@internationalizeDocstring class Dunno(plugins.ChannelIdDatabasePlugin): """This plugin was written initially to work with MoobotFactoids, the two of them to provide a similar-to-moobot-and-blootbot interface for factoids. diff --git a/plugins/Factoids/config.py b/plugins/Factoids/config.py index 79b43b654..6fdce802f 100644 --- a/plugins/Factoids/config.py +++ b/plugins/Factoids/config.py @@ -30,6 +30,8 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Factoids') def configure(advanced): # This will be called by supybot to configure this module. advanced is @@ -46,22 +48,22 @@ class FactoidFormat(registry.TemplatedString): Factoids = conf.registerPlugin('Factoids') conf.registerChannelValue(Factoids, 'learnSeparator', - registry.String('as', """Determines what separator must be used in the + registry.String('as', _("""Determines what separator must be used in the learn command. Defaults to 'as' -- learn as . Users might feel more comfortable with 'is' or something else, so it's - configurable.""")) + configurable."""))) conf.registerChannelValue(Factoids, 'showFactoidIfOnlyOneMatch', - registry.Boolean(True, """Determines whether the bot will reply with the + registry.Boolean(True, _("""Determines whether the bot will reply with the single matching factoid if only one factoid matches when using the search - command.""")) + command."""))) conf.registerChannelValue(Factoids, 'replyWhenInvalidCommand', - registry.Boolean(True, """Determines whether the bot will reply to invalid + registry.Boolean(True, _("""Determines whether the bot will reply to invalid commands by searching for a factoid; basically making the whatis - unnecessary when you want all factoids for a given key.""")) + unnecessary when you want all factoids for a given key."""))) conf.registerChannelValue(Factoids, 'format', - FactoidFormat('$key could be $value.', """Determines the format of + FactoidFormat('$key could be $value.', _("""Determines the format of the response given when a factoid's value is requested. All the standard substitutes apply, in addition to "$key" for the factoid's key and "$value" - for the factoid's value.""")) + for the factoid's value."""))) # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/Factoids/messages.pot b/plugins/Factoids/messages.pot new file mode 100644 index 000000000..2c40af2e8 --- /dev/null +++ b/plugins/Factoids/messages.pot @@ -0,0 +1,198 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-17 10:59+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: config.py:45 +#, docstring +msgid "" +"Value must include $value, otherwise the factoid's value would be left\n" +" out." +msgstr "" + +#: config.py:51 +msgid "" +"Determines what separator must be used in the\n" +" learn command. Defaults to 'as' -- learn as . Users might\n" +" feel more comfortable with 'is' or something else, so it's\n" +" configurable." +msgstr "" + +#: config.py:56 +msgid "" +"Determines whether the bot will reply with the\n" +" single matching factoid if only one factoid matches when using the search\n" +" command." +msgstr "" + +#: config.py:60 +msgid "" +"Determines whether the bot will reply to invalid\n" +" commands by searching for a factoid; basically making the whatis\n" +" unnecessary when you want all factoids for a given key." +msgstr "" + +#: config.py:64 +msgid "" +"Determines the format of\n" +" the response given when a factoid's value is requested. All the standard\n" +" substitutes apply, in addition to \"$key\" for the factoid's key and \"$value\"\n" +" for the factoid's value." +msgstr "" + +#: plugin.py:153 +msgid "" +"[] %s \n" +"\n" +" Associates with . is only\n" +" necessary if the message isn't sent on the channel\n" +" itself. The word '%s' is necessary to separate the\n" +" key from the value. It can be changed to another word\n" +" via the learnSeparator registry value.\n" +" " +msgstr "" + +#: plugin.py:179 +msgid "That's not a valid number for that key." +msgstr "" + +#: plugin.py:199 plugin.py:345 +msgid "No factoid matches that key." +msgstr "" + +#: plugin.py:211 +#, docstring +msgid "" +"[] []\n" +"\n" +" Looks up the value of in the factoid database. If given a\n" +" number, will return only that exact factoid. is only\n" +" necessary if the message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:222 plugin.py:273 plugin.py:384 +msgid "key id" +msgstr "" + +#: plugin.py:230 +#, docstring +msgid "" +"[] \n" +"\n" +" Locks the factoid(s) associated with so that they cannot be\n" +" removed or added to. is only necessary if the message isn't\n" +" sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:245 +#, docstring +msgid "" +"[] \n" +"\n" +" Unlocks the factoid(s) associated with so that they can be\n" +" removed or added to. is only necessary if the message isn't\n" +" sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:260 +#, docstring +msgid "" +"[] [|*]\n" +"\n" +" Removes the factoid from the factoids database. If there are\n" +" more than one factoid with such a key, a number is necessary to\n" +" determine which one should be removed. A * can be used to remove all\n" +" factoids associated with a key. is only necessary if\n" +" the message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:285 +msgid "There is no such factoid." +msgstr "" + +#: plugin.py:298 +msgid "Invalid factoid number." +msgstr "" + +#: plugin.py:304 +msgid "%s factoids have that key. Please specify which one to remove, or use * to designate all of them." +msgstr "" + +#: plugin.py:312 +#, docstring +msgid "" +"[]\n" +"\n" +" Returns a random factoid from the database for . \n" +" is only necessary if the message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:330 +msgid "I couldn't find a factoid." +msgstr "" + +#: plugin.py:335 +#, docstring +msgid "" +"[] \n" +"\n" +" Gives information about the factoid(s) associated with .\n" +" is only necessary if the message isn't sent in the channel\n" +" itself.\n" +" " +msgstr "" + +#: plugin.py:358 +msgid "#%i was added by %s at %s" +msgstr "" + +#: plugin.py:369 +#, docstring +msgid "" +"[] \n" +"\n" +" Changes the factoid # associated with according to\n" +" .\n" +" " +msgstr "" + +#: plugin.py:381 +msgid "I couldn't find any key %q" +msgstr "" + +#: plugin.py:396 +#, docstring +msgid "" +"[] [--values] [--{regexp} ] [ ...]\n" +"\n" +" Searches the keyspace for keys matching . If --regexp is given,\n" +" it associated value is taken as a regexp and matched against the keys.\n" +" If --values is given, search the value space instead of the keyspace.\n" +" " +msgstr "" + +#: plugin.py:431 +msgid "No keys matched that query." +msgstr "" + +#: plugin.py:436 +msgid "More than 100 keys matched that query; please narrow your query." +msgstr "" + diff --git a/plugins/Factoids/plugin.py b/plugins/Factoids/plugin.py index c375d16c8..bb4c68452 100644 --- a/plugins/Factoids/plugin.py +++ b/plugins/Factoids/plugin.py @@ -39,6 +39,8 @@ from supybot.commands import * import supybot.plugins as plugins import supybot.ircutils as ircutils import supybot.callbacks as callbacks +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Factoids') try: import sqlite @@ -148,14 +150,14 @@ class Factoids(callbacks.Plugin, plugins.ChannelDBHandler): else: irc.error('That factoid is locked.') learn = wrap(learn, ['factoid']) - learn._fake__doc__ = """[] %s + learn._fake__doc__ = _("""[] %s Associates with . is only necessary if the message isn't sent on the channel itself. The word '%s' is necessary to separate the key from the value. It can be changed to another word via the learnSeparator registry value. - """ + """) def _lookupFactoid(self, channel, key): @@ -174,7 +176,7 @@ class Factoids(callbacks.Plugin, plugins.ChannelDBHandler): try: irc.reply(factoids[number-1]) except IndexError: - irc.error('That\'s not a valid number for that key.') + irc.error(_('That\'s not a valid number for that key.')) return else: env = {'key': key} @@ -194,7 +196,7 @@ class Factoids(callbacks.Plugin, plugins.ChannelDBHandler): irc.replies(factoidsS, prefixer=prefixer, joiner=', or ', onlyPrefixFirst=True) elif error: - irc.error('No factoid matches that key.') + irc.error(_('No factoid matches that key.')) def invalidCommand(self, irc, msg, tokens): if irc.isChannel(msg.args[0]): @@ -204,6 +206,7 @@ class Factoids(callbacks.Plugin, plugins.ChannelDBHandler): factoids = self._lookupFactoid(channel, key) self._replyFactoids(irc, msg, key, factoids, error=False) + @internationalizeDocstring def whatis(self, irc, msg, args, channel, words): """[] [] @@ -216,12 +219,13 @@ class Factoids(callbacks.Plugin, plugins.ChannelDBHandler): if words[-1].isdigit(): number = int(words.pop()) if number <= 0: - irc.errorInvalid('key id') + irc.errorInvalid(_('key id')) key = ' '.join(words) factoids = self._lookupFactoid(channel, key) self._replyFactoids(irc, msg, key, factoids, number) whatis = wrap(whatis, ['channel', many('something')]) + @internationalizeDocstring def lock(self, irc, msg, args, channel, key): """[] @@ -236,6 +240,7 @@ class Factoids(callbacks.Plugin, plugins.ChannelDBHandler): irc.replySuccess() lock = wrap(lock, ['channel', 'text']) + @internationalizeDocstring def unlock(self, irc, msg, args, channel, key): """[] @@ -250,6 +255,7 @@ class Factoids(callbacks.Plugin, plugins.ChannelDBHandler): irc.replySuccess() unlock = wrap(unlock, ['channel', 'text']) + @internationalizeDocstring def forget(self, irc, msg, args, channel, words): """[] [|*] @@ -264,7 +270,7 @@ class Factoids(callbacks.Plugin, plugins.ChannelDBHandler): if words[-1].isdigit(): number = int(words.pop()) if number <= 0: - irc.errorInvalid('key id') + irc.errorInvalid(_('key id')) elif words[-1] == '*': words.pop() number = True @@ -276,7 +282,7 @@ class Factoids(callbacks.Plugin, plugins.ChannelDBHandler): WHERE key LIKE %s AND factoids.key_id=keys.id""", key) if cursor.rowcount == 0: - irc.error('There is no such factoid.') + irc.error(_('There is no such factoid.')) elif cursor.rowcount == 1 or number is True: (id, _) = cursor.fetchone() cursor.execute("""DELETE FROM factoids WHERE key_id=%s""", id) @@ -289,18 +295,19 @@ class Factoids(callbacks.Plugin, plugins.ChannelDBHandler): try: (_, id) = results[number-1] except IndexError: - irc.error('Invalid factoid number.') + irc.error(_('Invalid factoid number.')) return cursor.execute("DELETE FROM factoids WHERE id=%s", id) db.commit() irc.replySuccess() else: - irc.error('%s factoids have that key. ' + irc.error(_('%s factoids have that key. ' 'Please specify which one to remove, ' - 'or use * to designate all of them.' % + 'or use * to designate all of them.') % cursor.rowcount) forget = wrap(forget, ['channel', many('something')]) + @internationalizeDocstring def random(self, irc, msg, args, channel): """[] @@ -320,9 +327,10 @@ class Factoids(callbacks.Plugin, plugins.ChannelDBHandler): L.append('"%s": %s' % (ircutils.bold(key), factoid)) irc.reply('; '.join(L)) else: - irc.error('I couldn\'t find a factoid.') + irc.error(_('I couldn\'t find a factoid.')) random = wrap(random, ['channel']) + @internationalizeDocstring def info(self, irc, msg, args, channel, key): """[] @@ -334,7 +342,7 @@ class Factoids(callbacks.Plugin, plugins.ChannelDBHandler): cursor = db.cursor() cursor.execute("SELECT id, locked FROM keys WHERE key LIKE %s", key) if cursor.rowcount == 0: - irc.error('No factoid matches that key.') + irc.error(_('No factoid matches that key.')) return (id, locked) = map(int, cursor.fetchone()) cursor.execute("""SELECT added_by, added_at FROM factoids @@ -347,7 +355,7 @@ class Factoids(callbacks.Plugin, plugins.ChannelDBHandler): counter += 1 added_at = time.strftime(conf.supybot.reply.format.time(), time.localtime(int(added_at))) - L.append(format('#%i was added by %s at %s', + L.append(format(_('#%i was added by %s at %s'), counter, added_by, added_at)) factoids = '; '.join(L) s = format('Key %q is %s and has %n associated with it: %s', @@ -356,6 +364,7 @@ class Factoids(callbacks.Plugin, plugins.ChannelDBHandler): irc.reply(s) info = wrap(info, ['channel', 'text']) + @internationalizeDocstring def change(self, irc, msg, args, channel, key, number, replacer): """[] @@ -369,10 +378,10 @@ class Factoids(callbacks.Plugin, plugins.ChannelDBHandler): WHERE keys.key LIKE %s AND keys.id=factoids.key_id""", key) if cursor.rowcount == 0: - irc.error(format('I couldn\'t find any key %q', key)) + irc.error(format(_('I couldn\'t find any key %q'), key)) return elif cursor.rowcount < number: - irc.errorInvalid('key id') + irc.errorInvalid(_('key id')) (id, fact) = cursor.fetchall()[number-1] newfact = replacer(fact) cursor.execute("UPDATE factoids SET fact=%s WHERE id=%s", newfact, id) @@ -382,6 +391,7 @@ class Factoids(callbacks.Plugin, plugins.ChannelDBHandler): 'factoidId', 'regexpReplacer']) _sqlTrans = string.maketrans('*?', '%_') + @internationalizeDocstring def search(self, irc, msg, args, channel, optlist, globs): """[] [--values] [--{regexp} ] [ ...] @@ -418,13 +428,13 @@ class Factoids(callbacks.Plugin, plugins.ChannelDBHandler): sql = sql.replace('TARGET', target) cursor.execute(sql, formats) if cursor.rowcount == 0: - irc.reply('No keys matched that query.') + irc.reply(_('No keys matched that query.')) elif cursor.rowcount == 1 and \ self.registryValue('showFactoidIfOnlyOneMatch', channel): self.whatis(irc, msg, [channel, cursor.fetchone()[0]]) elif cursor.rowcount > 100: - irc.reply('More than 100 keys matched that query; ' - 'please narrow your query.') + irc.reply(_('More than 100 keys matched that query; ' + 'please narrow your query.')) else: keys = [repr(t[0]) for t in cursor.fetchall()] s = format('%L', keys) From 9917232d9aa535f202e63c967ffb55ace9bb1918 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sun, 17 Oct 2010 14:50:31 +0200 Subject: [PATCH 026/136] Internationalize Filter, Format, Games, and Google --- plugins/Filter/config.py | 18 +- plugins/Filter/messages.pot | 568 ++++++++++++++++++++++++++++++++++++ plugins/Filter/plugin.py | 114 +++++--- plugins/Format/config.py | 2 + plugins/Format/messages.pot | 170 +++++++++++ plugins/Format/plugin.py | 24 +- plugins/Games/config.py | 2 + plugins/Games/messages.pot | 128 ++++++++ plugins/Games/plugin.py | 68 +++-- plugins/Google/config.py | 34 ++- plugins/Google/messages.pot | 206 +++++++++++++ plugins/Google/plugin.py | 32 +- 12 files changed, 1255 insertions(+), 111 deletions(-) create mode 100644 plugins/Filter/messages.pot create mode 100644 plugins/Format/messages.pot create mode 100644 plugins/Games/messages.pot create mode 100644 plugins/Google/messages.pot diff --git a/plugins/Filter/config.py b/plugins/Filter/config.py index dee646c16..cd45fa9cb 100644 --- a/plugins/Filter/config.py +++ b/plugins/Filter/config.py @@ -29,22 +29,24 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Filter') Filter = conf.registerPlugin('Filter') conf.registerGroup(Filter, 'spellit') conf.registerGlobalValue(Filter.spellit, - 'replaceLetters', registry.Boolean(True, """Determines whether or not to - replace letters in the output of spellit.""")) + 'replaceLetters', registry.Boolean(True, _("""Determines whether or not to + replace letters in the output of spellit."""))) conf.registerGlobalValue(Filter.spellit, - 'replacePunctuation', registry.Boolean(True, """Determines whether or not - to replace punctuation in the output of spellit.""")) + 'replacePunctuation', registry.Boolean(True, _("""Determines whether or not + to replace punctuation in the output of spellit."""))) conf.registerGlobalValue(Filter.spellit, - 'replaceNumbers', registry.Boolean(True, """Determines whether or not to - replace numbers in the output of spellit.""")) + 'replaceNumbers', registry.Boolean(True, _("""Determines whether or not to + replace numbers in the output of spellit."""))) conf.registerGroup(Filter, 'shrink') conf.registerChannelValue(Filter.shrink, 'minimum', - registry.PositiveInteger(4, """Determines the minimum number of a letters - in a word before it will be shrunken by the shrink command/filter.""")) + registry.PositiveInteger(4, _("""Determines the minimum number of a letters + in a word before it will be shrunken by the shrink command/filter."""))) def configure(advanced): # This will be called by supybot to configure this module. advanced is diff --git a/plugins/Filter/messages.pot b/plugins/Filter/messages.pot new file mode 100644 index 000000000..ffde04b05 --- /dev/null +++ b/plugins/Filter/messages.pot @@ -0,0 +1,568 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-17 11:48+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: config.py:38 +msgid "" +"Determines whether or not to\n" +" replace letters in the output of spellit." +msgstr "" + +#: config.py:41 +msgid "" +"Determines whether or not\n" +" to replace punctuation in the output of spellit." +msgstr "" + +#: config.py:44 +msgid "" +"Determines whether or not to\n" +" replace numbers in the output of spellit." +msgstr "" + +#: config.py:48 +msgid "" +"Determines the minimum number of a letters\n" +" in a word before it will be shrunken by the shrink command/filter." +msgstr "" + +#: plugin.py:51 +#, docstring +msgid "" +"This plugin offers several commands which transform text in some way.\n" +" It also provides the capability of using such commands to 'filter' the\n" +" output of the bot -- for instance, you could make everything the bot says\n" +" be in leetspeak, or Morse code, or any number of other kinds of filters.\n" +" Not very useful, but definitely quite fun :)" +msgstr "" + +#: plugin.py:85 +#, docstring +msgid "" +"[] []\n" +"\n" +" Sets the outFilter of this plugin to be . If no command is\n" +" given, unsets the outFilter. is only necessary if the\n" +" message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:98 +msgid "That's not a valid filter command." +msgstr "" + +#: plugin.py:108 +#, docstring +msgid "" +"\n" +"\n" +" Removes all the vowels from . (If you're curious why this is\n" +" named 'hebrew' it's because I (jemfinch) thought of it in Hebrew class,\n" +" and printed Hebrew often elides the vowels.)\n" +" " +msgstr "" + +#: plugin.py:120 +#, docstring +msgid "" +"\n" +"\n" +" Removes all the spaces from .\n" +" " +msgstr "" + +#: plugin.py:130 +#, docstring +msgid "" +"\n" +"\n" +" Returns , with all consecutive duplicated letters removed.\n" +" " +msgstr "" + +#: plugin.py:143 +#, docstring +msgid "" +"\n" +"\n" +" Returns the binary representation of .\n" +" " +msgstr "" + +#: plugin.py:169 +#, docstring +msgid "" +"\n" +"\n" +" Returns a hexstring from the given string; a hexstring is a string\n" +" composed of the hexadecimal value of each character in the string\n" +" " +msgstr "" + +#: plugin.py:179 +#, docstring +msgid "" +"\n" +"\n" +" Returns the string corresponding to . Obviously,\n" +" must be a string of hexadecimal digits.\n" +" " +msgstr "" + +#: plugin.py:187 +msgid "Invalid input." +msgstr "" + +#: plugin.py:192 +#, docstring +msgid "" +"\n" +"\n" +" Rotates 13 characters to the right in the alphabet. Rot13 is\n" +" commonly used for text that simply needs to be hidden from inadvertent\n" +" reading by roaming eyes, since it's easily reversible.\n" +" " +msgstr "" + +#: plugin.py:203 +#, docstring +msgid "" +"\n" +"\n" +" Returns the lisping version of \n" +" " +msgstr "" + +#: plugin.py:234 +#, docstring +msgid "" +"\n" +"\n" +" Returns the l33tspeak version of \n" +" " +msgstr "" + +#: plugin.py:254 +#, docstring +msgid "" +"\n" +"\n" +" Replies with an especially k-rad translation of .\n" +" " +msgstr "" + +#: plugin.py:270 +#, docstring +msgid "" +"\n" +"\n" +" Replies with a string where each word is scrambled; i.e., each internal\n" +" letter (that is, all letters but the first and last) are shuffled.\n" +" " +msgstr "" + +#: plugin.py:335 +#, docstring +msgid "" +"\n" +"\n" +" Does the reverse of the morse command.\n" +" " +msgstr "" + +#: plugin.py:352 +#, docstring +msgid "" +"\n" +"\n" +" Gives the Morse code equivalent of a given string.\n" +" " +msgstr "" + +#: plugin.py:364 +#, docstring +msgid "" +"\n" +"\n" +" Reverses .\n" +" " +msgstr "" + +#: plugin.py:381 +#, docstring +msgid "" +"\n" +"\n" +" Returns with each character randomly colorized.\n" +" " +msgstr "" + +#: plugin.py:391 +#, docstring +msgid "" +"\n" +"\n" +" Returns colorized like a rainbow.\n" +" " +msgstr "" + +#: plugin.py:402 +#, docstring +msgid "" +"\n" +"\n" +" Returns stripped of all color codes.\n" +" " +msgstr "" + +#: plugin.py:411 +#, docstring +msgid "" +"\n" +"\n" +" Returns as if an AOLuser had said it.\n" +" " +msgstr "" + +#: plugin.py:438 +#, docstring +msgid "" +"\n" +"\n" +" Returns as if JeffK had said it himself.\n" +" " +msgstr "" + +#: plugin.py:534 +msgid "ay" +msgstr "" + +#: plugin.py:534 +msgid "bee" +msgstr "" + +#: plugin.py:534 +msgid "dee" +msgstr "" + +#: plugin.py:534 +msgid "see" +msgstr "" + +#: plugin.py:535 +msgid "aych" +msgstr "" + +#: plugin.py:535 +msgid "ee" +msgstr "" + +#: plugin.py:535 +msgid "eff" +msgstr "" + +#: plugin.py:535 +msgid "gee" +msgstr "" + +#: plugin.py:536 +msgid "ell" +msgstr "" + +#: plugin.py:536 +msgid "eye" +msgstr "" + +#: plugin.py:536 +msgid "jay" +msgstr "" + +#: plugin.py:536 +msgid "kay" +msgstr "" + +#: plugin.py:537 +msgid "cue" +msgstr "" + +#: plugin.py:537 +msgid "em" +msgstr "" + +#: plugin.py:537 +msgid "en" +msgstr "" + +#: plugin.py:537 +msgid "oh" +msgstr "" + +#: plugin.py:537 +msgid "pee" +msgstr "" + +#: plugin.py:538 +msgid "arr" +msgstr "" + +#: plugin.py:538 +msgid "ess" +msgstr "" + +#: plugin.py:538 +msgid "tee" +msgstr "" + +#: plugin.py:538 +msgid "you" +msgstr "" + +#: plugin.py:539 +msgid "double-you" +msgstr "" + +#: plugin.py:539 +msgid "ecks" +msgstr "" + +#: plugin.py:539 +msgid "vee" +msgstr "" + +#: plugin.py:539 +msgid "why" +msgstr "" + +#: plugin.py:540 +msgid "zee" +msgstr "" + +#: plugin.py:545 +msgid "exclamation point" +msgstr "" + +#: plugin.py:546 +msgid "quote" +msgstr "" + +#: plugin.py:547 +msgid "pound" +msgstr "" + +#: plugin.py:548 +msgid "dollar sign" +msgstr "" + +#: plugin.py:549 +msgid "percent" +msgstr "" + +#: plugin.py:550 +msgid "ampersand" +msgstr "" + +#: plugin.py:551 +msgid "single quote" +msgstr "" + +#: plugin.py:552 +msgid "left paren" +msgstr "" + +#: plugin.py:553 +msgid "right paren" +msgstr "" + +#: plugin.py:554 +msgid "asterisk" +msgstr "" + +#: plugin.py:555 +msgid "plus" +msgstr "" + +#: plugin.py:556 +msgid "comma" +msgstr "" + +#: plugin.py:557 +msgid "minus" +msgstr "" + +#: plugin.py:558 +msgid "period" +msgstr "" + +#: plugin.py:559 +msgid "slash" +msgstr "" + +#: plugin.py:560 +msgid "colon" +msgstr "" + +#: plugin.py:561 +msgid "semicolon" +msgstr "" + +#: plugin.py:562 +msgid "less than" +msgstr "" + +#: plugin.py:563 +msgid "equals" +msgstr "" + +#: plugin.py:564 +msgid "greater than" +msgstr "" + +#: plugin.py:565 +msgid "question mark" +msgstr "" + +#: plugin.py:566 +msgid "at" +msgstr "" + +#: plugin.py:567 +msgid "left bracket" +msgstr "" + +#: plugin.py:568 +msgid "backslash" +msgstr "" + +#: plugin.py:569 +msgid "right bracket" +msgstr "" + +#: plugin.py:570 +msgid "caret" +msgstr "" + +#: plugin.py:571 +msgid "underscore" +msgstr "" + +#: plugin.py:572 +msgid "backtick" +msgstr "" + +#: plugin.py:573 +msgid "left brace" +msgstr "" + +#: plugin.py:574 +msgid "pipe" +msgstr "" + +#: plugin.py:575 +msgid "right brace" +msgstr "" + +#: plugin.py:576 +msgid "tilde" +msgstr "" + +#: plugin.py:579 +msgid "one" +msgstr "" + +#: plugin.py:579 +msgid "three" +msgstr "" + +#: plugin.py:579 +msgid "two" +msgstr "" + +#: plugin.py:579 +msgid "zero" +msgstr "" + +#: plugin.py:580 +msgid "five" +msgstr "" + +#: plugin.py:580 +msgid "four" +msgstr "" + +#: plugin.py:580 +msgid "seven" +msgstr "" + +#: plugin.py:580 +msgid "six" +msgstr "" + +#: plugin.py:581 +msgid "eight" +msgstr "" + +#: plugin.py:581 +msgid "nine" +msgstr "" + +#: plugin.py:585 +#, docstring +msgid "" +"\n" +"\n" +" Returns , phonetically spelled out.\n" +" " +msgstr "" + +#: plugin.py:615 +#, docstring +msgid "" +"\n" +"\n" +" Returns as GNU/RMS would say it.\n" +" " +msgstr "" + +#: plugin.py:624 +#, docstring +msgid "" +"\n" +"\n" +" Returns with each word longer than\n" +" supybot.plugins.Filter.shrink.minimum being shrunken (i.e., like\n" +" \"internationalization\" becomes \"i18n\").\n" +" " +msgstr "" + +#: plugin.py:643 +#, docstring +msgid "" +"\n" +"\n" +" Returns with the l's made into r's and r's made into l's.\n" +" " +msgstr "" + +#: plugin.py:692 +#, docstring +msgid "" +"\n" +"\n" +" Returns rotated 180 degrees. Only really works for ASCII\n" +" printable characters.\n" +" " +msgstr "" + diff --git a/plugins/Filter/plugin.py b/plugins/Filter/plugin.py index 23b6277ff..28c302b7e 100644 --- a/plugins/Filter/plugin.py +++ b/plugins/Filter/plugin.py @@ -39,11 +39,14 @@ from supybot.commands import * import supybot.ircmsgs as ircmsgs import supybot.ircutils as ircutils import supybot.callbacks as callbacks +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Filter') class MyFilterProxy(object): def reply(self, s): self.s = s +@internationalizeDocstring class Filter(callbacks.Plugin): """This plugin offers several commands which transform text in some way. It also provides the capability of using such commands to 'filter' the @@ -77,6 +80,7 @@ class Filter(callbacks.Plugin): 'scramble', 'morse', 'reverse', 'colorize', 'squish', 'supa1337', 'colorstrip', 'aol', 'rainbow', 'spellit', 'hebrew', 'undup', 'gnu', 'shrink', 'azn', 'uniud'] + @internationalizeDocstring def outfilter(self, irc, msg, args, channel, command): """[] [] @@ -91,7 +95,7 @@ class Filter(callbacks.Plugin): self.outFilters.setdefault(channel, []).append(method) irc.replySuccess() else: - irc.error('That\'s not a valid filter command.') + irc.error(_('That\'s not a valid filter command.')) else: self.outFilters[channel] = [] irc.replySuccess() @@ -99,6 +103,7 @@ class Filter(callbacks.Plugin): [('checkChannelCapability', 'op'), additional('commandName')]) + @internationalizeDocstring def hebrew(self, irc, msg, args, text): """ @@ -110,6 +115,7 @@ class Filter(callbacks.Plugin): irc.reply(text) hebrew = wrap(hebrew, ['text']) + @internationalizeDocstring def squish(self, irc, msg, args, text): """ @@ -119,6 +125,7 @@ class Filter(callbacks.Plugin): irc.reply(text) squish = wrap(squish, ['text']) + @internationalizeDocstring def undup(self, irc, msg, args, text): """ @@ -131,6 +138,7 @@ class Filter(callbacks.Plugin): irc.reply(''.join(L)) undup = wrap(undup, ['text']) + @internationalizeDocstring def binary(self, irc, msg, args, text): """ @@ -156,6 +164,7 @@ class Filter(callbacks.Plugin): irc.reply(''.join(L)) binary = wrap(binary, ['text']) + @internationalizeDocstring def hexlify(self, irc, msg, args, text): """ @@ -165,6 +174,7 @@ class Filter(callbacks.Plugin): irc.reply(text.encode('hex_codec')) hexlify = wrap(hexlify, ['text']) + @internationalizeDocstring def unhexlify(self, irc, msg, args, text): """ @@ -174,9 +184,10 @@ class Filter(callbacks.Plugin): try: irc.reply(text.decode('hex_codec')) except TypeError: - irc.error('Invalid input.') + irc.error(_('Invalid input.')) unhexlify = wrap(unhexlify, ['text']) + @internationalizeDocstring def rot13(self, irc, msg, args, text): """ @@ -187,6 +198,7 @@ class Filter(callbacks.Plugin): irc.reply(text.encode('rot13')) rot13 = wrap(rot13, ['text']) + @internationalizeDocstring def lithp(self, irc, msg, args, text): """ @@ -217,6 +229,7 @@ class Filter(callbacks.Plugin): (re.compile(r'[aA][tT]'), '@'), (re.compile(r'[sS]\b'), 'z'), (re.compile(r'x'), '><'),] + @internationalizeDocstring def leet(self, irc, msg, args, text): """ @@ -236,6 +249,7 @@ class Filter(callbacks.Plugin): ('D', '|)'), ('B', '|3'), ('I', ']['), ('Vv', '\\/'), ('wW', '\\/\\/'), ('d', 'c|'), ('b', '|>'), ('c', '<'), ('h', '|n'),] + @internationalizeDocstring def supa1337(self, irc, msg, args, text): """ @@ -251,6 +265,7 @@ class Filter(callbacks.Plugin): _scrambleRe = re.compile(r'(?:\b|(?![a-zA-Z]))([a-zA-Z])([a-zA-Z]*)' r'([a-zA-Z])(?:\b|(?![a-zA-Z]))') + @internationalizeDocstring def scramble(self, irc, msg, args, text): """ @@ -315,6 +330,7 @@ class Filter(callbacks.Plugin): } _revMorseCode = dict([(y, x) for (x, y) in _morseCode.items()]) _unmorsere = re.compile('([.-]+)') + @internationalizeDocstring def unmorse(self, irc, msg, args, text): """ @@ -331,6 +347,7 @@ class Filter(callbacks.Plugin): irc.reply(text) unmorse = wrap(unmorse, ['text']) + @internationalizeDocstring def morse(self, irc, msg, args, text): """ @@ -342,6 +359,7 @@ class Filter(callbacks.Plugin): irc.reply(' '.join(L)) morse = wrap(morse, ['text']) + @internationalizeDocstring def reverse(self, irc, msg, args, text): """ @@ -350,6 +368,7 @@ class Filter(callbacks.Plugin): irc.reply(text[::-1]) reverse = wrap(reverse, ['text']) + @internationalizeDocstring def _color(self, c, fg=None): if c == ' ': return c @@ -357,6 +376,7 @@ class Filter(callbacks.Plugin): fg = str(random.randint(2, 15)).zfill(2) return '\x03%s%s' % (fg, c) + @internationalizeDocstring def colorize(self, irc, msg, args, text): """ @@ -366,6 +386,7 @@ class Filter(callbacks.Plugin): irc.reply('%s%s' % (''.join(L), '\x03')) colorize = wrap(colorize, ['text']) + @internationalizeDocstring def rainbow(self, irc, msg, args, text): """ @@ -376,6 +397,7 @@ class Filter(callbacks.Plugin): irc.reply(''.join(L) + '\x03') rainbow = wrap(rainbow, ['text']) + @internationalizeDocstring def stripcolor(self, irc, msg, args, text): """ @@ -384,6 +406,7 @@ class Filter(callbacks.Plugin): irc.reply(ircutils.stripColor(text)) stripcolor = wrap(stripcolor, ['text']) + @internationalizeDocstring def aol(self, irc, msg, args, text): """ @@ -410,6 +433,7 @@ class Filter(callbacks.Plugin): irc.reply(text) aol = wrap(aol, ['text']) + @internationalizeDocstring def jeffk(self, irc, msg, args, text): """ @@ -507,52 +531,56 @@ class Filter(callbacks.Plugin): # Keeping these separate so people can just replace the alphabets for # whatever their language of choice _spellLetters = { - 'a': 'ay', 'b': 'bee', 'c': 'see', 'd': 'dee', 'e': 'ee', 'f': 'eff', - 'g': 'gee', 'h': 'aych', 'i': 'eye', 'j': 'jay', 'k': 'kay', 'l': - 'ell', 'm': 'em', 'n': 'en', 'o': 'oh', 'p': 'pee', 'q': 'cue', 'r': - 'arr', 's': 'ess', 't': 'tee', 'u': 'you', 'v': 'vee', 'w': - 'double-you', 'x': 'ecks', 'y': 'why', 'z': 'zee' + 'a': _('ay'), 'b': _('bee'), 'c': _('see'), 'd': _('dee'), + 'e': _('ee'), 'f': _('eff'), 'g': _('gee'), 'h': _('aych'), + 'i': _('eye'), 'j': _('jay'), 'k': _('kay'), 'l': _('ell'), + 'm': _('em'), 'n': _('en'), 'o': _('oh'), 'p': _('pee'), 'q': _('cue'), + 'r': _('arr'), 's': _('ess'), 't': _('tee'), 'u': _('you'), + 'v': _('vee'), 'w': _('double-you'), 'x': _('ecks'), 'y': _('why'), + 'z': _('zee') } for (k, v) in _spellLetters.items(): _spellLetters[k.upper()] = v _spellPunctuation = { - '!': 'exclamation point', - '"': 'quote', - '#': 'pound', - '$': 'dollar sign', - '%': 'percent', - '&': 'ampersand', - '\'': 'single quote', - '(': 'left paren', - ')': 'right paren', - '*': 'asterisk', - '+': 'plus', - ',': 'comma', - '-': 'minus', - '.': 'period', - '/': 'slash', - ':': 'colon', - ';': 'semicolon', - '<': 'less than', - '=': 'equals', - '>': 'greater than', - '?': 'question mark', - '@': 'at', - '[': 'left bracket', - '\\': 'backslash', - ']': 'right bracket', - '^': 'caret', - '_': 'underscore', - '`': 'backtick', - '{': 'left brace', - '|': 'pipe', - '}': 'right brace', - '~': 'tilde' + '!': _('exclamation point'), + '"': _('quote'), + '#': _('pound'), + '$': _('dollar sign'), + '%': _('percent'), + '&': _('ampersand'), + '\'': _('single quote'), + '(': _('left paren'), + ')': _('right paren'), + '*': _('asterisk'), + '+': _('plus'), + ',': _('comma'), + '-': _('minus'), + '.': _('period'), + '/': _('slash'), + ':': _('colon'), + ';': _('semicolon'), + '<': _('less than'), + '=': _('equals'), + '>': _('greater than'), + '?': _('question mark'), + '@': _('at'), + '[': _('left bracket'), + '\\': _('backslash'), + ']': _('right bracket'), + '^': _('caret'), + '_': _('underscore'), + '`': _('backtick'), + '{': _('left brace'), + '|': _('pipe'), + '}': _('right brace'), + '~': _('tilde') } _spellNumbers = { - '0': 'zero', '1': 'one', '2': 'two', '3': 'three', '4': 'four', - '5': 'five', '6': 'six', '7': 'seven', '8': 'eight', '9': 'nine' + '0': _('zero'), '1': _('one'), '2': _('two'), '3': _('three'), + '4': _('four'), '5': _('five'), '6': _('six'), '7': _('seven'), + '8': _('eight'), '9': _('nine') } + @internationalizeDocstring def spellit(self, irc, msg, args, text): """ @@ -582,6 +610,7 @@ class Filter(callbacks.Plugin): irc.reply(out.getvalue()) spellit = wrap(spellit, ['text']) + @internationalizeDocstring def gnu(self, irc, msg, args, text): """ @@ -590,6 +619,7 @@ class Filter(callbacks.Plugin): irc.reply(' '.join(['GNU/' + s for s in text.split()])) gnu = wrap(gnu, ['text']) + @internationalizeDocstring def shrink(self, irc, msg, args, text): """ @@ -608,6 +638,7 @@ class Filter(callbacks.Plugin): shrink = wrap(shrink, ['text']) _azn_trans = string.maketrans('rlRL', 'lrLR') + @internationalizeDocstring def azn(self, irc, msg, args, text): """ @@ -656,6 +687,7 @@ class Filter(callbacks.Plugin): '_': u'\u203e', 'o': u'o', } + @internationalizeDocstring def uniud(self, irc, msg, args, text): """ diff --git a/plugins/Format/config.py b/plugins/Format/config.py index d27e3b988..5a0645d4c 100644 --- a/plugins/Format/config.py +++ b/plugins/Format/config.py @@ -29,6 +29,8 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Format') def configure(advanced): # This will be called by supybot to configure this module. advanced is diff --git a/plugins/Format/messages.pot b/plugins/Format/messages.pot new file mode 100644 index 000000000..960c03a14 --- /dev/null +++ b/plugins/Format/messages.pot @@ -0,0 +1,170 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-17 12:46+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: plugin.py:43 +#, docstring +msgid "" +"\n" +"\n" +" Returns bolded.\n" +" " +msgstr "" + +#: plugin.py:52 +#, docstring +msgid "" +"\n" +"\n" +" Returns in reverse-video.\n" +" " +msgstr "" + +#: plugin.py:61 +#, docstring +msgid "" +"\n" +"\n" +" Returns underlined.\n" +" " +msgstr "" + +#: plugin.py:70 +#, docstring +msgid "" +" [] \n" +"\n" +" Returns with foreground color and background color\n" +" (if given)\n" +" " +msgstr "" + +#: plugin.py:80 +#, docstring +msgid "" +" [ ...]\n" +"\n" +" Joins all the arguments together with .\n" +" " +msgstr "" + +#: plugin.py:89 +#, docstring +msgid "" +" \n" +"\n" +" Replaces with in\n" +" . The first and second arguments must necessarily be the same\n" +" length.\n" +" " +msgstr "" + +#: plugin.py:96 +msgid " must be the same length as ." +msgstr "" + +#: plugin.py:103 +#, docstring +msgid "" +"\n" +"\n" +" Returns uppercased.\n" +" " +msgstr "" + +#: plugin.py:112 +#, docstring +msgid "" +"\n" +"\n" +" Returns lowercased.\n" +" " +msgstr "" + +#: plugin.py:121 +#, docstring +msgid "" +"\n" +"\n" +" Returns capitalized.\n" +" " +msgstr "" + +#: plugin.py:130 +#, docstring +msgid "" +"\n" +"\n" +" Returns titlecased.\n" +" " +msgstr "" + +#: plugin.py:139 +#, docstring +msgid "" +"\n" +"\n" +" Returns the text surrounded by double quotes.\n" +" " +msgstr "" + +#: plugin.py:148 +#, docstring +msgid "" +" \n" +"\n" +" Concatenates two strings. Do keep in mind that this is *not* the same\n" +" thing as join \"\", since if contains spaces, they won't be\n" +" removed by concat.\n" +" " +msgstr "" + +#: plugin.py:159 +#, docstring +msgid "" +" \n" +"\n" +" Cuts down to by chopping off the rightmost characters in\n" +" excess of . If is a negative number, it chops that many\n" +" characters off the end of .\n" +" " +msgstr "" + +#: plugin.py:170 +#, docstring +msgid "" +" \n" +"\n" +" Returns the th space-separated field of . I.e., if text\n" +" is \"foo bar baz\" and is 2, \"bar\" is returned.\n" +" " +msgstr "" + +#: plugin.py:183 +#, docstring +msgid "" +" [ ...]\n" +"\n" +" Expands a Python-style format string using the remaining args. Just be\n" +" sure always to use %s, not %d or %f or whatever, because all the args\n" +" are strings.\n" +" " +msgstr "" + +#: plugin.py:197 +msgid "Not enough arguments for the format string." +msgstr "" + diff --git a/plugins/Format/plugin.py b/plugins/Format/plugin.py index 83c19115d..5e3e4611a 100644 --- a/plugins/Format/plugin.py +++ b/plugins/Format/plugin.py @@ -34,8 +34,11 @@ import supybot.utils as utils from supybot.commands import * import supybot.ircutils as ircutils import supybot.callbacks as callbacks +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Format') class Format(callbacks.Plugin): + @internationalizeDocstring def bold(self, irc, msg, args, text): """ @@ -44,6 +47,7 @@ class Format(callbacks.Plugin): irc.reply(ircutils.bold(text)) bold = wrap(bold, ['text']) + @internationalizeDocstring def reverse(self, irc, msg, args, text): """ @@ -52,6 +56,7 @@ class Format(callbacks.Plugin): irc.reply(ircutils.reverse(text)) reverse = wrap(reverse, ['text']) + @internationalizeDocstring def underline(self, irc, msg, args, text): """ @@ -60,6 +65,7 @@ class Format(callbacks.Plugin): irc.reply(ircutils.underline(text)) underline = wrap(underline, ['text']) + @internationalizeDocstring def color(self, irc, msg, args, fg, bg, text): """ [] @@ -69,6 +75,7 @@ class Format(callbacks.Plugin): irc.reply(ircutils.mircColor(text, fg=fg, bg=bg)) color = wrap(color, ['color', optional('color'), 'text']) + @internationalizeDocstring def join(self, irc, msg, args, sep): """ [ ...] @@ -77,6 +84,7 @@ class Format(callbacks.Plugin): irc.reply(sep.join(args)) join = wrap(join, ['anything'], allowExtra=True) + @internationalizeDocstring def translate(self, irc, msg, args, bad, good, text): """ @@ -85,11 +93,12 @@ class Format(callbacks.Plugin): length. """ if len(bad) != len(good): - irc.error(' must be the same length as ' - '.', Raise=True) + irc.error(_(' must be the same length as ' + '.'), Raise=True) irc.reply(text.translate(string.maketrans(bad, good))) translate = wrap(translate, ['something', 'something', 'text']) + @internationalizeDocstring def upper(self, irc, msg, args, text): """ @@ -98,6 +107,7 @@ class Format(callbacks.Plugin): irc.reply(text.upper()) upper = wrap(upper, ['text']) + @internationalizeDocstring def lower(self, irc, msg, args, text): """ @@ -106,6 +116,7 @@ class Format(callbacks.Plugin): irc.reply(text.lower()) lower = wrap(lower, ['text']) + @internationalizeDocstring def capitalize(self, irc, msg, args, text): """ @@ -114,6 +125,7 @@ class Format(callbacks.Plugin): irc.reply(text.capitalize()) capitalize = wrap(capitalize, ['text']) + @internationalizeDocstring def title(self, irc, msg, args, text): """ @@ -122,6 +134,7 @@ class Format(callbacks.Plugin): irc.reply(text.title()) title = wrap(title, ['text']) + @internationalizeDocstring def repr(self, irc, msg, args, text): """ @@ -130,6 +143,7 @@ class Format(callbacks.Plugin): irc.reply(utils.str.dqrepr(text)) repr = wrap(repr, ['text']) + @internationalizeDocstring def concat(self, irc, msg, args, first, second): """ @@ -140,6 +154,7 @@ class Format(callbacks.Plugin): irc.reply(first+second) concat = wrap(concat, ['something', 'text']) + @internationalizeDocstring def cut(self, irc, msg, args, size, text): """ @@ -150,6 +165,7 @@ class Format(callbacks.Plugin): irc.reply(text[:size]) cut = wrap(cut, ['int', 'text']) + @internationalizeDocstring def field(self, irc, msg, args, index, text): """ @@ -162,6 +178,7 @@ class Format(callbacks.Plugin): irc.errorInvalid('field') field = wrap(field, ['index', 'text']) + @internationalizeDocstring def format(self, irc, msg, args): """ [ ...] @@ -177,7 +194,8 @@ class Format(callbacks.Plugin): irc.reply(s) except TypeError, e: self.log.debug(utils.exnToString(e)) - irc.error('Not enough arguments for the format string.',Raise=True) + irc.error(_('Not enough arguments for the format string.'), + Raise=True) Class = Format diff --git a/plugins/Games/config.py b/plugins/Games/config.py index f5ec6bf7a..024d989d7 100644 --- a/plugins/Games/config.py +++ b/plugins/Games/config.py @@ -29,6 +29,8 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Games') def configure(advanced): # This will be called by supybot to configure this module. advanced is diff --git a/plugins/Games/messages.pot b/plugins/Games/messages.pot new file mode 100644 index 000000000..98aa2cc61 --- /dev/null +++ b/plugins/Games/messages.pot @@ -0,0 +1,128 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-17 13:16+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: plugin.py:46 +#, docstring +msgid "" +"takes no arguments\n" +"\n" +" Flips a coin and returns the result.\n" +" " +msgstr "" + +#: plugin.py:51 +msgid "heads" +msgstr "" + +#: plugin.py:53 +msgid "tails" +msgstr "" + +#: plugin.py:58 +#, docstring +msgid "" +"d\n" +"\n" +" Rolls a die with number of sides times.\n" +" For example, 2d6 will roll 2 six-sided dice; 10d10 will roll 10\n" +" ten-sided dice.\n" +" " +msgstr "" + +#: plugin.py:66 +msgid "You can't roll more than 1000 dice." +msgstr "" + +#: plugin.py:68 +msgid "Dice can't have more than 100 sides." +msgstr "" + +#: plugin.py:70 +msgid "Dice can't have fewer than 3 sides." +msgstr "" + +#: plugin.py:78 +msgid "Dice must be of the form d" +msgstr "" + +#: plugin.py:82 +msgid "It is possible.|Yes!|Of course.|Naturally.|Obviously.|It shall be.|The outlook is good.|It is so.|One would be wise to think so.|The answer is certainly yes." +msgstr "" + +#: plugin.py:86 +msgid "In your dreams.|I doubt it very much.|No chance.|The outlook is poor.|Unlikely.|About as likely as pigs flying.|You're kidding, right?|NO!|NO.|No.|The answer is a resounding no." +msgstr "" + +#: plugin.py:90 +msgid "Maybe...|No clue.|_I_ don't know.|The outlook is hazy, please ask again later.|What are you asking me for?|Come again?|You know the answer better than I.|The answer is def-- oooh! shiny thing!" +msgstr "" + +#: plugin.py:107 +#, docstring +msgid "" +"[]\n" +"\n" +" Ask a question and the answer shall be provided.\n" +" " +msgstr "" + +#: plugin.py:121 +#, docstring +msgid "" +"[spin]\n" +"\n" +" Fires the revolver. If the bullet was in the chamber, you're dead.\n" +" Tell me to spin the chambers and I will.\n" +" " +msgstr "" + +#: plugin.py:128 +msgid "*SPIN* Are you feeling lucky?" +msgstr "" + +#: plugin.py:137 +msgid "*BANG* Hey, who put a blank in here?!" +msgstr "" + +#: plugin.py:139 +msgid "reloads and spins the chambers." +msgstr "" + +#: plugin.py:141 +msgid "*click*" +msgstr "" + +#: plugin.py:148 +#, docstring +msgid "" +"[]\n" +"\n" +" Returns the number of consecutive lines you've sent in \n" +" without being interrupted by someone else (i.e. how long your current\n" +" 'monologue' is). is only necessary if the message isn't sent\n" +" in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:167 +msgid "Your current monologue is at least %n long." +msgstr "" + +#: plugin.py:168 +msgid "line" +msgstr "" + diff --git a/plugins/Games/plugin.py b/plugins/Games/plugin.py index 203bb9fd5..931c79d2d 100644 --- a/plugins/Games/plugin.py +++ b/plugins/Games/plugin.py @@ -36,20 +36,24 @@ from supybot.commands import * import supybot.ircmsgs as ircmsgs import supybot.ircutils as ircutils import supybot.callbacks as callbacks +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Games') class Games(callbacks.Plugin): + @internationalizeDocstring def coin(self, irc, msg, args): """takes no arguments Flips a coin and returns the result. """ if random.randrange(0, 2): - irc.reply('heads') + irc.reply(_('heads')) else: - irc.reply('tails') + irc.reply(_('tails')) coin = wrap(coin) + @internationalizeDocstring def dice(self, irc, msg, args, m): """d @@ -59,11 +63,11 @@ class Games(callbacks.Plugin): """ (dice, sides) = utils.iter.imap(int, m.groups()) if dice > 1000: - irc.error('You can\'t roll more than 1000 dice.') + irc.error(_('You can\'t roll more than 1000 dice.')) elif sides > 100: - irc.error('Dice can\'t have more than 100 sides.') + irc.error(_('Dice can\'t have more than 100 sides.')) elif sides < 3: - irc.error('Dice can\'t have fewer than 3 sides.') + irc.error(_('Dice can\'t have fewer than 3 sides.')) else: L = [0] * dice for i in xrange(dice): @@ -71,36 +75,34 @@ class Games(callbacks.Plugin): irc.reply(format('%L', [str(x) for x in L])) _dicere = re.compile(r'^(\d+)d(\d+)$') dice = wrap(dice, [('matches', _dicere, - 'Dice must be of the form d')]) + _('Dice must be of the form d'))]) # The list of words and algorithm are pulled straight the mozbot # MagicEightBall.bm module: http://tinyurl.com/7ytg7 - _responses = {'positive': ['It is possible.', 'Yes!', 'Of course.', - 'Naturally.', 'Obviously.', 'It shall be.', - 'The outlook is good.', 'It is so.', - 'One would be wise to think so.', - 'The answer is certainly yes.'], - 'negative': ['In your dreams.', 'I doubt it very much.', - 'No chance.', 'The outlook is poor.', - 'Unlikely.', 'About as likely as pigs flying.', - 'You\'re kidding, right?', 'NO!', 'NO.', 'No.', - 'The answer is a resounding no.', ], - 'unknown' : ['Maybe...', 'No clue.', '_I_ don\'t know.', - 'The outlook is hazy, please ask again later.', - 'What are you asking me for?', 'Come again?', - 'You know the answer better than I.', - 'The answer is def-- oooh! shiny thing!'], - } + _positive = _('It is possible.|Yes!|Of course.|Naturally.|Obviously.|' + 'It shall be.|The outlook is good.|It is so.|' + 'One would be wise to think so.|' + 'The answer is certainly yes.') + _negative = _('In your dreams.|I doubt it very much.|No chance.|' + 'The outlook is poor.|Unlikely.|' + 'About as likely as pigs flying.|You\'re kidding, right?|' + 'NO!|NO.|No.|The answer is a resounding no.') + _unknown = _('Maybe...|No clue.|_I_ don\'t know.|' + 'The outlook is hazy, please ask again later.|' + 'What are you asking me for?|Come again?|' + 'You know the answer better than I.|' + 'The answer is def-- oooh! shiny thing!') def _checkTheBall(self, questionLength): if questionLength % 3 == 0: - category = 'positive' + catalog = self._positive elif questionLength % 3 == 1: - category = 'negative' + catalog = self._negative else: - category = 'unknown' - return utils.iter.choice(self._responses[category]) + catalog = self._unknown + return utils.iter.choice(catalog.split('|')) + @internationalizeDocstring def eightball(self, irc, msg, args, text): """[] @@ -114,6 +116,7 @@ class Games(callbacks.Plugin): _rouletteChamber = random.randrange(0, 6) _rouletteBullet = random.randrange(0, 6) + @internationalizeDocstring def roulette(self, irc, msg, args, spin): """[spin] @@ -122,7 +125,7 @@ class Games(callbacks.Plugin): """ if spin: self._rouletteBullet = random.randrange(0, 6) - irc.reply('*SPIN* Are you feeling lucky?', prefixNick=False) + irc.reply(_('*SPIN* Are you feeling lucky?'), prefixNick=False) return channel = msg.args[0] if self._rouletteChamber == self._rouletteBullet: @@ -131,15 +134,16 @@ class Games(callbacks.Plugin): if irc.nick in irc.state.channels[channel].ops: irc.queueMsg(ircmsgs.kick(channel, msg.nick, 'BANG!')) else: - irc.reply('*BANG* Hey, who put a blank in here?!', + irc.reply(_('*BANG* Hey, who put a blank in here?!'), prefixNick=False) - irc.reply('reloads and spins the chambers.', action=True) + irc.reply(_('reloads and spins the chambers.'), action=True) else: - irc.reply('*click*') + irc.reply(_('*click*')) self._rouletteChamber += 1 self._rouletteChamber %= 6 roulette = wrap(roulette, ['public', additional(('literal', 'spin'))]) + @internationalizeDocstring def monologue(self, irc, msg, args, channel): """[] @@ -160,8 +164,8 @@ class Games(callbacks.Plugin): i += 1 else: break - irc.reply(format('Your current monologue is at least %n long.', - (i, 'line'))) + irc.reply(format(_('Your current monologue is at least %n long.'), + (i, _('line')))) monologue = wrap(monologue, ['channel']) Class = Games diff --git a/plugins/Google/config.py b/plugins/Google/config.py index a309911ab..cd1771a6d 100644 --- a/plugins/Google/config.py +++ b/plugins/Google/config.py @@ -30,15 +30,17 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Google') def configure(advanced): from supybot.questions import output, yn conf.registerPlugin('Google', True) - output("""The Google plugin has the functionality to watch for URLs + output(_("""The Google plugin has the functionality to watch for URLs that match a specific pattern. (We call this a snarfer) When supybot sees such a URL, it will parse the web page - for information and reply with the results.""") - if yn('Do you want the Google search snarfer enabled by default?'): + for information and reply with the results.""")) + if yn(_('Do you want the Google search snarfer enabled by default?')): conf.supybot.plugins.Google.searchSnarfer.setValue(True) class Language(registry.OnlySomeStrings): @@ -94,29 +96,29 @@ class SafeSearch(registry.OnlySomeStrings): Google = conf.registerPlugin('Google') conf.registerGlobalValue(Google, 'referer', - registry.String('', """Determines the URL that will be sent to Google for + registry.String('', _("""Determines the URL that will be sent to Google for the Referer field of the search requests. If this value is empty, a Referer will be generated in the following format: - http://$server/$botName""")) + http://$server/$botName"""))) conf.registerChannelValue(Google, 'searchSnarfer', - registry.Boolean(False, """Determines whether the search snarfer is + registry.Boolean(False, _("""Determines whether the search snarfer is enabled. If so, messages (even unaddressed ones) beginning with the word 'google' will result in the first URL Google returns being sent to the - channel.""")) + channel."""))) conf.registerChannelValue(Google, 'colorfulFilter', - registry.Boolean(False, """Determines whether the word 'google' in the - bot's output will be made colorful (like Google's logo).""")) + registry.Boolean(False, _("""Determines whether the word 'google' in the + bot's output will be made colorful (like Google's logo)."""))) conf.registerChannelValue(Google, 'bold', - registry.Boolean(True, """Determines whether results are bolded.""")) + registry.Boolean(True, _("""Determines whether results are bolded."""))) conf.registerChannelValue(Google, 'maximumResults', - NumSearchResults(8, """Determines the maximum number of results returned - from the google command.""")) + NumSearchResults(8, _("""Determines the maximum number of results returned + from the google command."""))) conf.registerChannelValue(Google, 'defaultLanguage', - Language('lang_en', """Determines what default language is used in - searches. If left empty, no specific language will be requested.""")) + Language('lang_'+ _('en'), _("""Determines what default language is used in + searches. If left empty, no specific language will be requested."""))) conf.registerChannelValue(Google, 'searchFilter', - SafeSearch('moderate', """Determines what level of search filtering to use + SafeSearch('moderate', _("""Determines what level of search filtering to use by default. 'active' - most filtering, 'moderate' - default filtering, - 'off' - no filtering""")) + 'off' - no filtering"""))) # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/Google/messages.pot b/plugins/Google/messages.pot new file mode 100644 index 000000000..9493dd858 --- /dev/null +++ b/plugins/Google/messages.pot @@ -0,0 +1,206 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-17 14:50+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: config.py:39 +msgid "" +"The Google plugin has the functionality to watch for URLs\n" +" that match a specific pattern. (We call this a snarfer)\n" +" When supybot sees such a URL, it will parse the web page\n" +" for information and reply with the results." +msgstr "" + +#: config.py:43 +msgid "Do you want the Google search snarfer enabled by default?" +msgstr "" + +#: config.py:88 +#, docstring +msgid "Value must be 1 <= n <= 8" +msgstr "" + +#: config.py:99 +msgid "" +"Determines the URL that will be sent to Google for\n" +" the Referer field of the search requests. If this value is empty, a\n" +" Referer will be generated in the following format:\n" +" http://$server/$botName" +msgstr "" + +#: config.py:104 +msgid "" +"Determines whether the search snarfer is\n" +" enabled. If so, messages (even unaddressed ones) beginning with the word\n" +" 'google' will result in the first URL Google returns being sent to the\n" +" channel." +msgstr "" + +#: config.py:109 +msgid "" +"Determines whether the word 'google' in the\n" +" bot's output will be made colorful (like Google's logo)." +msgstr "" + +#: config.py:112 +msgid "Determines whether results are bolded." +msgstr "" + +#: config.py:114 +msgid "" +"Determines the maximum number of results returned\n" +" from the google command." +msgstr "" + +#: config.py:117 +msgid "" +"Determines what default language is used in\n" +" searches. If left empty, no specific language will be requested." +msgstr "" + +#: config.py:117 +msgid "en" +msgstr "" + +#: config.py:120 +msgid "" +"Determines what level of search filtering to use\n" +" by default. 'active' - most filtering, 'moderate' - default filtering,\n" +" 'off' - no filtering" +msgstr "" + +#: plugin.py:101 +#, docstring +msgid "" +"Perform a search using Google's AJAX API.\n" +" search(\"search phrase\", options={})\n" +"\n" +" Valid options are:\n" +" smallsearch - True/False (Default: False)\n" +" filter - {active,moderate,off} (Default: \"moderate\")\n" +" language - Restrict search to documents in the given language\n" +" (Default: \"lang_en\")\n" +" " +msgstr "" + +#: plugin.py:141 plugin.py:192 +msgid "We broke The Google!" +msgstr "" + +#: plugin.py:161 +msgid "No matches found." +msgstr "" + +#: plugin.py:167 +#, docstring +msgid "" +"\n" +"\n" +" Does a google search, but only returns the first result.\n" +" " +msgstr "" + +#: plugin.py:176 +msgid "Google found nothing." +msgstr "" + +#: plugin.py:181 +#, docstring +msgid "" +" [--{filter,language} ]\n" +"\n" +" Searches google.com for the given string. As many results as can fit\n" +" are included. --language accepts a language abbreviation; --filter\n" +" accepts a filtering level ('active', 'moderate', 'off').\n" +" " +msgstr "" + +#: plugin.py:204 +#, docstring +msgid "" +"\n" +"\n" +" Returns a link to the cached version of if it is available.\n" +" " +msgstr "" + +#: plugin.py:215 +msgid "Google seems to have no cache for that site." +msgstr "" + +#: plugin.py:220 +#, docstring +msgid "" +" [ ...]\n" +"\n" +" Returns the results of each search, in order, from greatest number\n" +" of results to least.\n" +" " +msgstr "" + +#: plugin.py:244 +#, docstring +msgid "" +" [to] \n" +"\n" +" Returns translated from into .\n" +" Beware that translating to or from languages that use multi-byte\n" +" characters may result in some very odd results.\n" +" " +msgstr "" + +#: plugin.py:263 +msgid "from language" +msgstr "" + +#: plugin.py:264 plugin.py:273 +msgid "Valid languages are: %L" +msgstr "" + +#: plugin.py:272 +msgid "to language" +msgstr "" + +#: plugin.py:289 +#, docstring +msgid "^google\\s+(.*)$" +msgstr "" + +#: plugin.py:311 +#, docstring +msgid "" +"\n" +"\n" +" Uses Google's calculator to calculate the value of .\n" +" " +msgstr "" + +#: plugin.py:325 +msgid "Google's calculator didn't come up with anything." +msgstr "" + +#: plugin.py:331 +#, docstring +msgid "" +"\n" +"\n" +" Looks up on Google.\n" +" " +msgstr "" + +#: plugin.py:345 +msgid "Google's phonebook didn't come up with anything." +msgstr "" + diff --git a/plugins/Google/plugin.py b/plugins/Google/plugin.py index 2adef8ef3..a0d94d062 100644 --- a/plugins/Google/plugin.py +++ b/plugins/Google/plugin.py @@ -41,6 +41,8 @@ from supybot.commands import * import supybot.ircmsgs as ircmsgs import supybot.ircutils as ircutils import supybot.callbacks as callbacks +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Google') simplejson = None @@ -94,6 +96,7 @@ class Google(callbacks.PluginRegexp): return msg _gsearchUrl = 'http://ajax.googleapis.com/ajax/services/search/web' + @internationalizeDocstring def search(self, query, channel, options={}): """Perform a search using Google's AJAX API. search("search phrase", options={}) @@ -135,7 +138,7 @@ class Google(callbacks.PluginRegexp): json = simplejson.load(fd) fd.close() if json['responseStatus'] != 200: - raise callbacks.Error, 'We broke The Google!' + raise callbacks.Error, _('We broke The Google!') return json def formatData(self, data, bold=True, max=0): @@ -155,10 +158,11 @@ class Google(callbacks.PluginRegexp): else: results.append(url) if not results: - return format('No matches found.') + return format(_('No matches found.')) else: return format('; '.join(results)) + @internationalizeDocstring def lucky(self, irc, msg, args, text): """ @@ -169,9 +173,10 @@ class Google(callbacks.PluginRegexp): url = data['responseData']['results'][0]['unescapedUrl'] irc.reply(url.encode('utf-8')) else: - irc.reply('Google found nothing.') + irc.reply(_('Google found nothing.')) lucky = wrap(lucky, ['text']) + @internationalizeDocstring def google(self, irc, msg, args, optlist, text): """ [--{filter,language} ] @@ -184,7 +189,7 @@ class Google(callbacks.PluginRegexp): irc.errorInvalid('language') data = self.search(text, msg.args[0], dict(optlist)) if data['responseStatus'] != 200: - irc.reply('We broke The Google!') + irc.reply(_('We broke The Google!')) return bold = self.registryValue('bold', msg.args[0]) max = self.registryValue('maximumResults', msg.args[0]) @@ -194,6 +199,7 @@ class Google(callbacks.PluginRegexp): 'filter':''}), 'text']) + @internationalizeDocstring def cache(self, irc, msg, args, url): """ @@ -206,9 +212,10 @@ class Google(callbacks.PluginRegexp): url = m['cacheUrl'].encode('utf-8') irc.reply(url) return - irc.error('Google seems to have no cache for that site.') + irc.error(_('Google seems to have no cache for that site.')) cache = wrap(cache, ['url']) + @internationalizeDocstring def fight(self, irc, msg, args): """ [ ...] @@ -232,6 +239,7 @@ class Google(callbacks.PluginRegexp): irc.reply(s) _gtranslateUrl='http://ajax.googleapis.com/ajax/services/language/translate' + @internationalizeDocstring def translate(self, irc, msg, args, fromLang, toLang, text): """ [to] @@ -252,8 +260,8 @@ class Google(callbacks.PluginRegexp): fromLang = lang.transLangs[fromLang.capitalize()] elif lang.normalize('lang_'+fromLang)[5:] \ not in lang.transLangs.values(): - irc.errorInvalid('from language', fromLang, - format('Valid languages are: %L', + irc.errorInvalid(_('from language'), fromLang, + format(_('Valid languages are: %L'), lang.transLangs.keys())) else: fromLang = lang.normalize('lang_'+fromLang)[5:] @@ -261,8 +269,8 @@ class Google(callbacks.PluginRegexp): toLang = lang.transLangs[toLang.capitalize()] elif lang.normalize('lang_'+toLang)[5:] \ not in lang.transLangs.values(): - irc.errorInvalid('to language', toLang, - format('Valid languages are: %L', + irc.errorInvalid(_('to language'), toLang, + format(_('Valid languages are: %L'), lang.transLangs.keys())) else: toLang = lang.normalize('lang_'+toLang)[5:] @@ -298,6 +306,7 @@ class Google(callbacks.PluginRegexp): _calcSupRe = re.compile(r'(.*?)', re.I) _calcFontRe = re.compile(r'(.*?)') _calcTimesRe = re.compile(r'&(?:times|#215);') + @internationalizeDocstring def calc(self, irc, msg, args, expr): """ @@ -313,10 +322,11 @@ class Google(callbacks.PluginRegexp): s = self._calcTimesRe.sub(r'*', s) irc.reply(s) else: - irc.reply('Google\'s calculator didn\'t come up with anything.') + irc.reply(_('Google\'s calculator didn\'t come up with anything.')) calc = wrap(calc, ['text']) _phoneRe = re.compile(r'Phonebook.*?(.*?) @@ -332,7 +342,7 @@ class Google(callbacks.PluginRegexp): s = utils.web.htmlToText(s) irc.reply(s) else: - irc.reply('Google\'s phonebook didn\'t come up with anything.') + irc.reply(_('Google\'s phonebook didn\'t come up with anything.')) phonebook = wrap(phonebook, ['text']) From 203fe3c56a72d6df150f15fb1c18e5f43f006454 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sun, 17 Oct 2010 15:36:26 +0200 Subject: [PATCH 027/136] Internationalize Herald, Internet, Karma, Lart, Later, Limiter, Math, and Misc --- plugins/Herald/config.py | 34 ++--- plugins/Herald/messages.pot | 140 +++++++++++++++++++++ plugins/Herald/plugin.py | 13 +- plugins/Internet/config.py | 4 +- plugins/Internet/messages.pot | 85 +++++++++++++ plugins/Internet/plugin.py | 29 +++-- plugins/Karma/config.py | 26 ++-- plugins/Karma/messages.pot | 137 ++++++++++++++++++++ plugins/Karma/plugin.py | 28 +++-- plugins/Lart/config.py | 8 +- plugins/Lart/messages.pot | 54 ++++++++ plugins/Lart/plugin.py | 15 ++- plugins/Later/config.py | 11 +- plugins/Later/messages.pot | 101 +++++++++++++++ plugins/Later/plugin.py | 22 ++-- plugins/Limiter/config.py | 14 ++- plugins/Limiter/messages.pot | 50 ++++++++ plugins/Limiter/plugin.py | 4 +- plugins/Math/config.py | 4 +- plugins/Math/messages.pot | 129 +++++++++++++++++++ plugins/Math/plugin.py | 40 +++--- plugins/Misc/config.py | 18 +-- plugins/Misc/messages.pot | 230 ++++++++++++++++++++++++++++++++++ plugins/Misc/plugin.py | 71 ++++++----- 24 files changed, 1131 insertions(+), 136 deletions(-) create mode 100644 plugins/Herald/messages.pot create mode 100644 plugins/Internet/messages.pot create mode 100644 plugins/Karma/messages.pot create mode 100644 plugins/Lart/messages.pot create mode 100644 plugins/Later/messages.pot create mode 100644 plugins/Limiter/messages.pot create mode 100644 plugins/Math/messages.pot create mode 100644 plugins/Misc/messages.pot diff --git a/plugins/Herald/config.py b/plugins/Herald/config.py index 1f96464f8..7c5cbac1d 100644 --- a/plugins/Herald/config.py +++ b/plugins/Herald/config.py @@ -29,6 +29,8 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Heral') def configure(advanced): # This will be called by supybot to configure this module. advanced is @@ -41,31 +43,31 @@ def configure(advanced): Herald = conf.registerPlugin('Herald') conf.registerChannelValue(Herald, 'heralding', - registry.Boolean(True, """Determines whether messages will be sent to the + registry.Boolean(True, _("""Determines whether messages will be sent to the channel when a recognized user joins; basically enables or disables the - plugin.""")) + plugin."""))) conf.registerGlobalValue(Herald, 'requireCapability', - registry.String('', """Determines what capability (if any) is required to - add/change/remove the herald of another user.""")) + registry.String('', _("""Determines what capability (if any) is required to + add/change/remove the herald of another user."""))) conf.registerChannelValue(Herald, 'throttle', - registry.PositiveInteger(600, """Determines the minimum number of seconds - between heralds.""")) + registry.PositiveInteger(600, _("""Determines the minimum number of seconds + between heralds."""))) conf.registerChannelValue(Herald.throttle, 'afterPart', - registry.NonNegativeInteger(0, """Determines the minimum number of seconds + registry.NonNegativeInteger(0, _("""Determines the minimum number of seconds after parting that the bot will not herald the person when he or she - rejoins.""")) + rejoins."""))) conf.registerChannelValue(Herald.throttle, 'afterSplit', - registry.NonNegativeInteger(60, """Determines the minimum number of seconds - after a netsplit that the bot will not herald the users that split.""")) + registry.NonNegativeInteger(60, _("""Determines the minimum number of seconds + after a netsplit that the bot will not herald the users that split."""))) conf.registerChannelValue(Herald, 'default', - registry.String('', """Sets the default herald to use. If a user has a + registry.String('', _("""Sets the default herald to use. If a user has a personal herald specified, that will be used instead. If set to the empty - string, the default herald will be disabled.""")) + string, the default herald will be disabled."""))) conf.registerChannelValue(Herald.default, 'notice', - registry.Boolean(True, """Determines whether the default herald will be - sent as a NOTICE instead of a PRIVMSG.""")) + registry.Boolean(True, _("""Determines whether the default herald will be + sent as a NOTICE instead of a PRIVMSG."""))) conf.registerChannelValue(Herald.default, 'public', - registry.Boolean(False, """Determines whether the default herald will be - sent publicly.""")) + registry.Boolean(False, _("""Determines whether the default herald will be + sent publicly."""))) # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/Herald/messages.pot b/plugins/Herald/messages.pot new file mode 100644 index 000000000..61aec5e7d --- /dev/null +++ b/plugins/Herald/messages.pot @@ -0,0 +1,140 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-17 15:21+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: config.py:46 +msgid "" +"Determines whether messages will be sent to the\n" +" channel when a recognized user joins; basically enables or disables the\n" +" plugin." +msgstr "" + +#: config.py:50 +msgid "" +"Determines what capability (if any) is required to\n" +" add/change/remove the herald of another user." +msgstr "" + +#: config.py:53 +msgid "" +"Determines the minimum number of seconds\n" +" between heralds." +msgstr "" + +#: config.py:56 +msgid "" +"Determines the minimum number of seconds\n" +" after parting that the bot will not herald the person when he or she\n" +" rejoins." +msgstr "" + +#: config.py:60 +msgid "" +"Determines the minimum number of seconds\n" +" after a netsplit that the bot will not herald the users that split." +msgstr "" + +#: config.py:63 +msgid "" +"Sets the default herald to use. If a user has a\n" +" personal herald specified, that will be used instead. If set to the empty\n" +" string, the default herald will be disabled." +msgstr "" + +#: config.py:67 +msgid "" +"Determines whether the default herald will be\n" +" sent as a NOTICE instead of a PRIVMSG." +msgstr "" + +#: config.py:70 +msgid "" +"Determines whether the default herald will be\n" +" sent publicly." +msgstr "" + +#: plugin.py:143 +#, docstring +msgid "" +"[] [--remove|]\n" +"\n" +" If is given, sets the default herald to . A of \"\"\n" +" will remove the default herald. If is not given, returns the\n" +" current default herald. is only necessary if the message\n" +" isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:162 +msgid "I do not have a default herald set for %s." +msgstr "" + +#: plugin.py:170 +#, docstring +msgid "" +"[] []\n" +"\n" +" Returns the current herald message for (or the user\n" +" is currently identified or recognized as). If \n" +" is not given, defaults to the user giving the command. \n" +" is only necessary if the message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:181 +msgid "I have no herald for %s." +msgstr "" + +#: plugin.py:201 +#, docstring +msgid "" +"[] \n" +"\n" +" Sets the herald message for (or the user is\n" +" currently identified or recognized as) to . is only\n" +" necessary if the message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:214 +#, docstring +msgid "" +"[] []\n" +"\n" +" Removes the herald message set for , or the user\n" +" is currently identified or recognized as. If \n" +" is not given, defaults to the user giving the command.\n" +" is only necessary if the message isn't sent in the channel\n" +" itself.\n" +" " +msgstr "" + +#: plugin.py:227 +msgid "I have no herald for that user." +msgstr "" + +#: plugin.py:232 +#, docstring +msgid "" +"[] [] \n" +"\n" +" Changes the herald message for , or the user is\n" +" currently identified or recognized as, according to . If\n" +" is not given, defaults to the calling user. is only\n" +" necessary if the message isn't sent in the channel itself.\n" +" " +msgstr "" + diff --git a/plugins/Herald/plugin.py b/plugins/Herald/plugin.py index 5069c51e3..e4b99bd40 100644 --- a/plugins/Herald/plugin.py +++ b/plugins/Herald/plugin.py @@ -40,6 +40,8 @@ import supybot.plugins as plugins import supybot.ircutils as ircutils import supybot.callbacks as callbacks from supybot.utils.structures import TimeoutQueue +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Heral') filename = conf.supybot.directories.data.dirize('Herald.db') @@ -136,6 +138,7 @@ class Herald(callbacks.Plugin): raise KeyError return id + @internationalizeDocstring def default(self, irc, msg, args, channel, optlist, text): """[] [--remove|] @@ -156,12 +159,13 @@ class Herald(callbacks.Plugin): irc.replySuccess() else: resp = self.registryValue('default', channel) or \ - 'I do not have a default herald set for %s.' % channel + _('I do not have a default herald set for %s.') % channel irc.reply(resp) default = wrap(default, ['channel', getopts({'remove': ''}), additional('text')]) + @internationalizeDocstring def get(self, irc, msg, args, channel, user): """[] [] @@ -174,7 +178,7 @@ class Herald(callbacks.Plugin): herald = self.db[channel, user.id] irc.reply(herald) except KeyError: - irc.error('I have no herald for %s.' % user.name) + irc.error(_('I have no herald for %s.') % user.name) get = wrap(get, ['channel', first('otherUser', 'user')]) def _preCheck(self, irc, msg, user): @@ -192,6 +196,7 @@ class Herald(callbacks.Plugin): # I chose not to make optional in this command because # if it's not a valid username (e.g., if the user tyops and misspells a # username), it may be nice not to clobber the user's herald. + @internationalizeDocstring def add(self, irc, msg, args, channel, user, herald): """[] @@ -204,6 +209,7 @@ class Herald(callbacks.Plugin): irc.replySuccess() add = wrap(add, ['channel', 'otherUser', 'text']) + @internationalizeDocstring def remove(self, irc, msg, args, channel, user): """[] [] @@ -218,9 +224,10 @@ class Herald(callbacks.Plugin): del self.db[channel, user.id] irc.replySuccess() except KeyError: - irc.error('I have no herald for that user.') + irc.error(_('I have no herald for that user.')) remove = wrap(remove, ['channel', first('otherUser', 'user')]) + @internationalizeDocstring def change(self, irc, msg, args, channel, user, changer): """[] [] diff --git a/plugins/Internet/config.py b/plugins/Internet/config.py index 118f805a2..cf7ff92c6 100644 --- a/plugins/Internet/config.py +++ b/plugins/Internet/config.py @@ -29,6 +29,8 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Internet') def configure(advanced): # This will be called by supybot to configure this module. advanced is @@ -42,7 +44,7 @@ def configure(advanced): Internet = conf.registerPlugin('Internet') # This is where your configuration variables (if any) should go. For example: # conf.registerGlobalValue(Internet, 'someConfigVariableName', -# registry.Boolean(False, """Help for someConfigVariableName.""")) +# registry.Boolean(False, _("""Help for someConfigVariableName."""))) # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/Internet/messages.pot b/plugins/Internet/messages.pot new file mode 100644 index 000000000..5f6f82699 --- /dev/null +++ b/plugins/Internet/messages.pot @@ -0,0 +1,85 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-17 15:20+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: plugin.py:43 +#, docstring +msgid "Add the help for \"@help Internet\" here." +msgstr "" + +#: plugin.py:47 +#, docstring +msgid "" +"\n" +"\n" +" Returns the ip of or the reverse DNS hostname of .\n" +" " +msgstr "" + +#: plugin.py:54 plugin.py:61 plugin.py:65 +msgid "Host not found." +msgstr "" + +#: plugin.py:77 +#, docstring +msgid "" +"\n" +"\n" +" Returns WHOIS information on the registration of .\n" +" " +msgstr "" + +#: plugin.py:83 +msgid "domain" +msgstr "" + +#: plugin.py:112 +msgid "updated %s" +msgstr "" + +#: plugin.py:115 +msgid "registered %s" +msgstr "" + +#: plugin.py:118 +msgid "expires %s" +msgstr "" + +#: plugin.py:138 +msgid " " +msgstr "" + +#: plugin.py:140 +msgid " " +msgstr "" + +#: plugin.py:145 +msgid "%s%s is %L." +msgstr "" + +#: plugin.py:148 +msgid "I couldn't find such a domain." +msgstr "" + +#: plugin.py:153 +#, docstring +msgid "" +"\n" +"\n" +" Returns the hexadecimal IP for that IP.\n" +" " +msgstr "" + diff --git a/plugins/Internet/plugin.py b/plugins/Internet/plugin.py index 490c9165c..4c33dcf3b 100644 --- a/plugins/Internet/plugin.py +++ b/plugins/Internet/plugin.py @@ -35,11 +35,14 @@ import supybot.utils as utils from supybot.commands import * from supybot.utils.iter import any import supybot.callbacks as callbacks +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Internet') - +@internationalizeDocstring class Internet(callbacks.Plugin): """Add the help for "@help Internet" here.""" threaded = True + @internationalizeDocstring def dns(self, irc, msg, args, host): """ @@ -48,18 +51,18 @@ class Internet(callbacks.Plugin): if utils.net.isIP(host): hostname = socket.getfqdn(host) if hostname == host: - irc.reply('Host not found.') + irc.reply(_('Host not found.')) else: irc.reply(hostname) else: try: ip = socket.gethostbyname(host) if ip == '64.94.110.11': # Verisign sucks! - irc.reply('Host not found.') + irc.reply(_('Host not found.')) else: irc.reply(ip) except socket.error: - irc.reply('Host not found.') + irc.reply(_('Host not found.')) dns = wrap(dns, ['something']) _domain = ['Domain Name', 'Server Name', 'domain'] @@ -69,6 +72,7 @@ class Internet(callbacks.Plugin): _created = ['Created On', 'Domain Registration Date', 'Creation Date'] _expires = ['Expiration Date', 'Domain Expiration Date'] _status = ['Status', 'Domain Status', 'status'] + @internationalizeDocstring def whois(self, irc, msg, args, domain): """ @@ -76,7 +80,7 @@ class Internet(callbacks.Plugin): """ usertld = domain.split('.')[-1] if '.' not in domain: - irc.errorInvalid('domain') + irc.errorInvalid(_('domain')) return try: t = telnetlib.Telnet('%s.whois-servers.net' % usertld, 43) @@ -105,13 +109,13 @@ class Internet(callbacks.Plugin): registrar = ':'.join(line.split(':')[1:]).strip() elif not updated and any(line.startswith, self._updated): s = ':'.join(line.split(':')[1:]).strip() - updated = 'updated %s' % s + updated = _('updated %s') % s elif not created and any(line.startswith, self._created): s = ':'.join(line.split(':')[1:]).strip() - created = 'registered %s' % s + created = _('registered %s') % s elif not expires and any(line.startswith, self._expires): s = ':'.join(line.split(':')[1:]).strip() - expires = 'expires %s' % s + expires = _('expires %s') % s elif not status and any(line.startswith, self._status): status = ':'.join(line.split(':')[1:]).strip().lower() if not status: @@ -131,19 +135,20 @@ class Internet(callbacks.Plugin): if not line: continue if line.startswith('Email'): - url = ' ' % line.split('@')[-1] + url = _(' ') % line.split('@')[-1] elif line.startswith('Registrar Organization:'): - url = ' ' % line.split(':')[1].strip() + url = _(' ') % line.split(':')[1].strip() elif line == 'Not a valid ID pattern': url = '' if server and status: info = filter(None, [status, created, updated, expires]) - s = format('%s%s is %L.', server, url, info) + s = format(_('%s%s is %L.'), server, url, info) irc.reply(s) else: - irc.error('I couldn\'t find such a domain.') + irc.error(_('I couldn\'t find such a domain.')) whois = wrap(whois, ['lowered']) + @internationalizeDocstring def hexip(self, irc, msg, args, ip): """ diff --git a/plugins/Karma/config.py b/plugins/Karma/config.py index f2ae97196..b43dd011c 100644 --- a/plugins/Karma/config.py +++ b/plugins/Karma/config.py @@ -29,6 +29,8 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Karma') def configure(advanced): # This will be called by supybot to configure this module. advanced is @@ -41,22 +43,22 @@ def configure(advanced): conf.registerPlugin('Karma') conf.registerChannelValue(conf.supybot.plugins.Karma, 'simpleOutput', - registry.Boolean(False, """Determines whether the bot will output shorter - versions of the karma output when requesting a single thing's karma.""")) + registry.Boolean(False, _("""Determines whether the bot will output shorter + versions of the karma output when requesting a single thing's karma."""))) conf.registerChannelValue(conf.supybot.plugins.Karma, 'response', - registry.Boolean(False, """Determines whether the bot will reply with a - success message when something's karma is increased or decreased.""")) + registry.Boolean(False, _("""Determines whether the bot will reply with a + success message when something's karma is increased or decreased."""))) conf.registerChannelValue(conf.supybot.plugins.Karma, 'rankingDisplay', - registry.Integer(3, """Determines how many highest/lowest karma things are - shown when karma is called with no arguments.""")) + registry.Integer(3, _("""Determines how many highest/lowest karma things + are shown when karma is called with no arguments."""))) conf.registerChannelValue(conf.supybot.plugins.Karma, 'mostDisplay', - registry.Integer(25, """Determines how many karma things are shown when - the most command is called.'""")) + registry.Integer(25, _("""Determines how many karma things are shown when + the most command is called.'"""))) conf.registerChannelValue(conf.supybot.plugins.Karma, 'allowSelfRating', - registry.Boolean(False, """Determines whether users can adjust the karma - of their nick.""")) + registry.Boolean(False, _("""Determines whether users can adjust the karma + of their nick."""))) conf.registerChannelValue(conf.supybot.plugins.Karma, 'allowUnaddressedKarma', - registry.Boolean(False, """Determines whether the bot will - increase/decrease karma without being addressed.""")) + registry.Boolean(False, _("""Determines whether the bot will + increase/decrease karma without being addressed."""))) # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/Karma/messages.pot b/plugins/Karma/messages.pot new file mode 100644 index 000000000..8fd166f04 --- /dev/null +++ b/plugins/Karma/messages.pot @@ -0,0 +1,137 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-17 15:21+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: config.py:46 +msgid "" +"Determines whether the bot will output shorter\n" +" versions of the karma output when requesting a single thing's karma." +msgstr "" + +#: config.py:49 +msgid "" +"Determines whether the bot will reply with a\n" +" success message when something's karma is increased or decreased." +msgstr "" + +#: config.py:52 +msgid "" +"Determines how many highest/lowest karma things\n" +" are shown when karma is called with no arguments." +msgstr "" + +#: config.py:55 +msgid "" +"Determines how many karma things are shown when\n" +" the most command is called.'" +msgstr "" + +#: config.py:58 +msgid "" +"Determines whether users can adjust the karma\n" +" of their nick." +msgstr "" + +#: config.py:61 +msgid "" +"Determines whether the bot will\n" +" increase/decrease karma without being addressed." +msgstr "" + +#: plugin.py:243 plugin.py:251 +msgid "You're not allowed to adjust your own karma." +msgstr "" + +#: plugin.py:280 +#, docstring +msgid "" +"[] [ ...]\n" +"\n" +" Returns the karma of . If is not given, returns the top\n" +" N karmas, where N is determined by the config variable\n" +" supybot.plugins.Karma.rankingDisplay. If one is given, returns\n" +" the details of its karma; if more than one is given, returns\n" +" the total karma of each of the the things. is only necessary\n" +" if the message isn't sent on the channel itself.\n" +" " +msgstr "" + +#: plugin.py:293 +msgid "%s has neutral karma." +msgstr "" + +#: plugin.py:300 +msgid "Karma for %q has been increased %n and decreased %n for a total karma of %s." +msgstr "" + +#: plugin.py:315 +msgid "I didn't know the karma for any of those things." +msgstr "" + +#: plugin.py:325 plugin.py:354 +msgid "I have no karma for this channel." +msgstr "" + +#: plugin.py:330 +msgid " You (%s) are ranked %i out of %i." +msgstr "" + +#: plugin.py:334 +msgid "Highest karma: %L. Lowest karma: %L.%s" +msgstr "" + +#: plugin.py:342 +#, docstring +msgid "" +"[] {increased,decreased,active}\n" +"\n" +" Returns the most increased, the most decreased, or the most active\n" +" (the sum of increased and decreased) karma things. is only\n" +" necessary if the message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:360 +#, docstring +msgid "" +"[] \n" +"\n" +" Resets the karma of to 0.\n" +" " +msgstr "" + +#: plugin.py:370 +#, docstring +msgid "" +"[] \n" +"\n" +" Dumps the Karma database for to in the bot's\n" +" data directory. is only necessary if the message isn't sent\n" +" in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:382 +#, docstring +msgid "" +"[] \n" +"\n" +" Loads the Karma database for from in the bot's\n" +" data directory. is only necessary if the message isn't sent\n" +" in the channel itself.\n" +" " +msgstr "" + diff --git a/plugins/Karma/plugin.py b/plugins/Karma/plugin.py index 02570b5af..af2191619 100644 --- a/plugins/Karma/plugin.py +++ b/plugins/Karma/plugin.py @@ -38,6 +38,8 @@ import supybot.plugins as plugins import supybot.ircmsgs as ircmsgs import supybot.ircutils as ircutils import supybot.callbacks as callbacks +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Karma') class SqliteKarmaDB(object): def __init__(self, filename): @@ -238,7 +240,7 @@ class Karma(callbacks.Plugin): thing = thing[:-2] if ircutils.strEqual(thing, irc.msg.nick) and \ not self.registryValue('allowSelfRating', channel): - irc.error('You\'re not allowed to adjust your own karma.') + irc.error(_('You\'re not allowed to adjust your own karma.')) elif thing: self.db.increment(channel, self._normalizeThing(thing)) self._respond(irc, channel) @@ -246,7 +248,7 @@ class Karma(callbacks.Plugin): thing = thing[:-2] if ircutils.strEqual(thing, irc.msg.nick) and \ not self.registryValue('allowSelfRating', channel): - irc.error('You\'re not allowed to adjust your own karma.') + irc.error(_('You\'re not allowed to adjust your own karma.')) elif thing: self.db.decrement(channel, self._normalizeThing(thing)) self._respond(irc, channel) @@ -273,6 +275,7 @@ class Karma(callbacks.Plugin): if thing[-2:] in ('++', '--'): self._doKarma(irc, channel, thing) + @internationalizeDocstring def karma(self, irc, msg, args, channel, things): """[] [ ...] @@ -287,15 +290,15 @@ class Karma(callbacks.Plugin): name = things[0] t = self.db.get(channel, name) if t is None: - irc.reply(format('%s has neutral karma.', name)) + irc.reply(format(_('%s has neutral karma.'), name)) else: (added, subtracted) = t total = added - subtracted if self.registryValue('simpleOutput', channel): s = format('%s: %i', name, total) else: - s = format('Karma for %q has been increased %n and ' - 'decreased %n for a total karma of %s.', + s = format(_('Karma for %q has been increased %n and ' + 'decreased %n for a total karma of %s.'), name, (added, 'time'), (subtracted, 'time'), total) irc.reply(s) @@ -309,7 +312,8 @@ class Karma(callbacks.Plugin): s += neutral irc.reply(s + '.') else: - irc.reply('I didn\'t know the karma for any of those things.') + irc.reply(_('I didn\'t know the karma for any of those ' + 'things.')) else: # No name was given. Return the top/bottom N karmas. limit = self.registryValue('rankingDisplay', channel) top = self.db.top(channel, limit) @@ -318,21 +322,22 @@ class Karma(callbacks.Plugin): lowest = [format('%q (%s)', s, t) for (s, t) in self.db.bottom(channel, limit)] if not (highest and lowest): - irc.error('I have no karma for this channel.') + irc.error(_('I have no karma for this channel.')) return rank = self.db.rank(channel, msg.nick) if rank is not None: total = self.db.size(channel) - rankS = format(' You (%s) are ranked %i out of %i.', + rankS = format(_(' You (%s) are ranked %i out of %i.'), msg.nick, rank, total) else: rankS = '' - s = format('Highest karma: %L. Lowest karma: %L.%s', + s = format(_('Highest karma: %L. Lowest karma: %L.%s'), highest, lowest, rankS) irc.reply(s) karma = wrap(karma, ['channel', any('something')]) _mostAbbrev = utils.abbrev(['increased', 'decreased', 'active']) + @internationalizeDocstring def most(self, irc, msg, args, channel, kind): """[] {increased,decreased,active} @@ -346,10 +351,11 @@ class Karma(callbacks.Plugin): L = [format('%q: %i', name, i) for (name, i) in L] irc.reply(format('%L', L)) else: - irc.error('I have no karma for this channel.') + irc.error(_('I have no karma for this channel.')) most = wrap(most, ['channel', ('literal', ['increased', 'decreased', 'active'])]) + @internationalizeDocstring def clear(self, irc, msg, args, channel, name): """[] @@ -359,6 +365,7 @@ class Karma(callbacks.Plugin): irc.replySuccess() clear = wrap(clear, [('checkChannelCapability', 'op'), 'text']) + @internationalizeDocstring def dump(self, irc, msg, args, channel, filename): """[] @@ -370,6 +377,7 @@ class Karma(callbacks.Plugin): irc.replySuccess() dump = wrap(dump, [('checkCapability', 'owner'), 'channeldb', 'filename']) + @internationalizeDocstring def load(self, irc, msg, args, channel, filename): """[] diff --git a/plugins/Lart/config.py b/plugins/Lart/config.py index f3c83e7a7..3ad4aab1f 100644 --- a/plugins/Lart/config.py +++ b/plugins/Lart/config.py @@ -29,6 +29,8 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Lart') def configure(advanced): # This will be called by supybot to configure this module. advanced is @@ -42,10 +44,10 @@ def configure(advanced): Lart = conf.registerPlugin('Lart') # This is where your configuration variables (if any) should go. For example: # conf.registerGlobalValue(Lart, 'someConfigVariableName', -# registry.Boolean(False, """Help for someConfigVariableName.""")) +# registry.Boolean(False, _("""Help for someConfigVariableName."""))) conf.registerChannelValue(Lart, 'showIds', - registry.Boolean(False, """Determines whether the bot will show the ids of - a lart when the lart is given.""")) + registry.Boolean(False, _("""Determines whether the bot will show the ids + of a lart when the lart is given."""))) # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/Lart/messages.pot b/plugins/Lart/messages.pot new file mode 100644 index 000000000..a290a2605 --- /dev/null +++ b/plugins/Lart/messages.pot @@ -0,0 +1,54 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-17 15:21+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: config.py:49 +msgid "" +"Determines whether the bot will show the ids\n" +" of a lart when the lart is given." +msgstr "" + +#: plugin.py:48 +msgid "Larts must contain $who." +msgstr "" + +#: plugin.py:52 +#, docstring +msgid "" +"[] [] [for ]\n" +"\n" +" Uses the Luser Attitude Readjustment Tool on (for ,\n" +" if given). If is given, uses that specific lart. is\n" +" only necessary if the message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:66 +msgid "There is no lart with id #%i." +msgstr "" + +#: plugin.py:71 +msgid "There are no larts in my database for %s." +msgstr "" + +#: plugin.py:77 +msgid "trying to dis me" +msgstr "" + +#: plugin.py:85 +msgid " for " +msgstr "" + diff --git a/plugins/Lart/plugin.py b/plugins/Lart/plugin.py index 1d1c5c6a6..aed197614 100644 --- a/plugins/Lart/plugin.py +++ b/plugins/Lart/plugin.py @@ -32,6 +32,8 @@ import re from supybot.commands import * import supybot.plugins as plugins import supybot.ircutils as ircutils +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Lart') class Lart(plugins.ChannelIdDatabasePlugin): _meRe = re.compile(r'\bme\b', re.I) @@ -43,8 +45,9 @@ class Lart(plugins.ChannelIdDatabasePlugin): def addValidator(self, irc, text): if '$who' not in text: - irc.error('Larts must contain $who.', Raise=True) + irc.error(_('Larts must contain $who.'), Raise=True) + @internationalizeDocstring def lart(self, irc, msg, args, channel, id, text): """[] [] [for ] @@ -60,18 +63,18 @@ class Lart(plugins.ChannelIdDatabasePlugin): try: lart = self.db.get(channel, id) except KeyError: - irc.error(format('There is no lart with id #%i.', id)) + irc.error(format(_('There is no lart with id #%i.'), id)) return else: lart = self.db.random(channel) if not lart: - irc.error(format('There are no larts in my database ' - 'for %s.', channel)) + irc.error(format(_('There are no larts in my database ' + 'for %s.'), channel)) return text = lart.text if ircutils.strEqual(target, irc.nick): target = msg.nick - reason = self._replaceFirstPerson('trying to dis me', irc.nick) + reason = self._replaceFirstPerson(_('trying to dis me'), irc.nick) else: target = self._replaceFirstPerson(target, msg.nick) reason = self._replaceFirstPerson(reason, msg.nick) @@ -79,7 +82,7 @@ class Lart(plugins.ChannelIdDatabasePlugin): target = target.rstrip('.') text = text.replace('$who', target) if reason: - text += ' for ' + reason + text += _(' for ') + reason if self.registryValue('showIds', channel): text += format(' (#%i)', lart.id) irc.reply(text, action=True) diff --git a/plugins/Later/config.py b/plugins/Later/config.py index 97ddde7ca..fd1e96a92 100644 --- a/plugins/Later/config.py +++ b/plugins/Later/config.py @@ -29,6 +29,8 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Later') def configure(advanced): # This will be called by setup.py to configure this module. Advanced is @@ -40,10 +42,11 @@ def configure(advanced): Later = conf.registerPlugin('Later') conf.registerGlobalValue(Later, 'maximum', - registry.NonNegativeInteger(0, """Determines the maximum number of messages - to be queued for a user. If this value is 0, there is no maximum.""")) + registry.NonNegativeInteger(0, _("""Determines the maximum number of + messages to be queued for a user. If this value is 0, there is no maximum. + """))) conf.registerGlobalValue(Later, 'private', - registry.Boolean(True, """Determines whether users will be notified in the - first place in which they're seen, or in private.""")) + registry.Boolean(True, _("""Determines whether users will be notified in + the first place in which they're seen, or in private."""))) # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/Later/messages.pot b/plugins/Later/messages.pot new file mode 100644 index 000000000..719e65ef8 --- /dev/null +++ b/plugins/Later/messages.pot @@ -0,0 +1,101 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-17 15:21+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: config.py:45 +msgid "" +"Determines the maximum number of\n" +" messages to be queued for a user. If this value is 0, there is no maximum.\n" +" " +msgstr "" + +#: config.py:49 +msgid "" +"Determines whether users will be notified in\n" +" the first place in which they're seen, or in private." +msgstr "" + +#: plugin.py:47 +#, docstring +msgid "" +"Used to do things later; currently, it only allows the sending of\n" +" nick-based notes. Do note (haha!) that these notes are *not* private\n" +" and don't even pretend to be; if you want such features, consider using the\n" +" Note plugin." +msgstr "" + +#: plugin.py:87 +msgid "just now" +msgstr "" + +#: plugin.py:108 +#, docstring +msgid "" +" \n" +"\n" +" Tells the next time is in seen. can\n" +" contain wildcard characters, and the first matching nick will be\n" +" given the note.\n" +" " +msgstr "" + +#: plugin.py:115 +msgid "I can't send notes to myself." +msgstr "" + +#: plugin.py:121 +msgid "That person's message queue is already full." +msgstr "" + +#: plugin.py:126 +#, docstring +msgid "" +"[]\n" +"\n" +" If is given, replies with what notes are waiting on ,\n" +" otherwise, replies with the nicks that have notes waiting for them.\n" +" " +msgstr "" + +#: plugin.py:137 +msgid "I have no notes for that nick." +msgstr "" + +#: plugin.py:142 +msgid "I currently have notes waiting for %L." +msgstr "" + +#: plugin.py:145 +msgid "I have no notes waiting to be delivered." +msgstr "" + +#: plugin.py:150 +#, docstring +msgid "" +"\n" +"\n" +" Removes the notes waiting on .\n" +" " +msgstr "" + +#: plugin.py:159 +msgid "There were no notes for %r" +msgstr "" + +#: plugin.py:183 +msgid "Sent %s: <%s> %s" +msgstr "" + diff --git a/plugins/Later/plugin.py b/plugins/Later/plugin.py index 3babf6365..3601a47e2 100644 --- a/plugins/Later/plugin.py +++ b/plugins/Later/plugin.py @@ -38,8 +38,11 @@ from supybot.commands import * import supybot.ircmsgs as ircmsgs import supybot.ircutils as ircutils import supybot.callbacks as callbacks +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Later') +@internationalizeDocstring class Later(callbacks.Plugin): """Used to do things later; currently, it only allows the sending of nick-based notes. Do note (haha!) that these notes are *not* private @@ -81,7 +84,7 @@ class Later(callbacks.Plugin): try: return utils.timeElapsed(diff, seconds=False) + ' ago' except ValueError: - return 'just now' + return _('just now') def _addNote(self, nick, whence, text, at=None, maximum=None): if at is None: @@ -100,6 +103,7 @@ class Later(callbacks.Plugin): self.wildcards.append(nick) self._flushNotes() + @internationalizeDocstring def tell(self, irc, msg, args, nick, text): """ @@ -108,15 +112,16 @@ class Later(callbacks.Plugin): given the note. """ if ircutils.strEqual(nick, irc.nick): - irc.error('I can\'t send notes to myself.') + irc.error(_('I can\'t send notes to myself.')) return try: self._addNote(nick, msg.nick, text) irc.replySuccess() except ValueError: - irc.error('That person\'s message queue is already full.') + irc.error(_('That person\'s message queue is already full.')) tell = wrap(tell, ['something', 'text']) + @internationalizeDocstring def notes(self, irc, msg, args, nick): """[] @@ -129,17 +134,18 @@ class Later(callbacks.Plugin): for (when, whence, note) in self._notes[nick]] irc.reply(format('%L', notes)) else: - irc.error('I have no notes for that nick.') + irc.error(_('I have no notes for that nick.')) else: nicks = self._notes.keys() if nicks: utils.sortBy(ircutils.toLower, nicks) - irc.reply(format('I currently have notes waiting for %L.', + irc.reply(format(_('I currently have notes waiting for %L.'), nicks)) else: - irc.error('I have no notes waiting to be delivered.') + irc.error(_('I have no notes waiting to be delivered.')) notes = wrap(notes, [additional('something')]) + @internationalizeDocstring def remove(self, irc, msg, args, nick): """ @@ -150,7 +156,7 @@ class Later(callbacks.Plugin): self._flushNotes() irc.replySuccess() except KeyError: - irc.error('There were no notes for %r' % nick) + irc.error(_('There were no notes for %r') % nick) remove = wrap(remove, [('checkCapability', 'admin'), 'something']) def doPrivmsg(self, irc, msg): @@ -174,7 +180,7 @@ class Later(callbacks.Plugin): self._flushNotes() def _formatNote(self, when, whence, note): - return 'Sent %s: <%s> %s' % (self._timestamp(when), whence, note) + return _('Sent %s: <%s> %s') % (self._timestamp(when), whence, note) diff --git a/plugins/Limiter/config.py b/plugins/Limiter/config.py index a28f8a5b4..64905d240 100644 --- a/plugins/Limiter/config.py +++ b/plugins/Limiter/config.py @@ -29,6 +29,8 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Limiter') def configure(advanced): # This will be called by supybot to configure this module. advanced is @@ -41,17 +43,17 @@ def configure(advanced): Limiter = conf.registerPlugin('Limiter') conf.registerChannelValue(Limiter, 'enable', - registry.Boolean(False, """Determines whether the bot will maintain the + registry.Boolean(False, _("""Determines whether the bot will maintain the channel limit to be slightly above the current number of people in the - channel, in order to make clone/drone attacks harder.""")) + channel, in order to make clone/drone attacks harder."""))) conf.registerChannelValue(Limiter, 'minimumExcess', - registry.PositiveInteger(5, """Determines the minimum number of free + registry.PositiveInteger(5, _("""Determines the minimum number of free spots that will be saved when limits are being enforced. This should - always be smaller than supybot.plugins.Limiter.limit.maximumExcess.""")) + always be smaller than supybot.plugins.Limiter.limit.maximumExcess."""))) conf.registerChannelValue(Limiter, 'maximumExcess', - registry.PositiveInteger(10, """Determines the maximum number of free spots + registry.PositiveInteger(10, _("""Determines the maximum number of free spots that will be saved when limits are being enforced. This should always be - larger than supybot.plugins.Limiter.limit.minimumExcess.""")) + larger than supybot.plugins.Limiter.limit.minimumExcess."""))) # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/Limiter/messages.pot b/plugins/Limiter/messages.pot new file mode 100644 index 000000000..4ec916ea3 --- /dev/null +++ b/plugins/Limiter/messages.pot @@ -0,0 +1,50 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-17 15:35+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: config.py:46 +msgid "" +"Determines whether the bot will maintain the\n" +" channel limit to be slightly above the current number of people in the\n" +" channel, in order to make clone/drone attacks harder." +msgstr "" + +#: config.py:50 +msgid "" +"Determines the minimum number of free\n" +" spots that will be saved when limits are being enforced. This should\n" +" always be smaller than supybot.plugins.Limiter.limit.maximumExcess." +msgstr "" + +#: config.py:54 +msgid "" +"Determines the maximum number of free spots\n" +" that will be saved when limits are being enforced. This should always be\n" +" larger than supybot.plugins.Limiter.limit.minimumExcess." +msgstr "" + +#: plugin.py:40 +#, docstring +msgid "" +"In order to use this plugin, its config values need to be properly\n" +" setup. supybot.plugins.Limiter.enable needs to be set to True and\n" +" supybot.plugins.Limiter.{maximumExcess,minimumExcess} should be set to\n" +" values appropriate to your channel (if the defaults aren't satisfactory.\n" +" Once these are set, and someone enters/leaves the channel, Supybot will\n" +" start setting the proper +l modes.\n" +" " +msgstr "" + diff --git a/plugins/Limiter/plugin.py b/plugins/Limiter/plugin.py index c3e02cc11..6388c4327 100644 --- a/plugins/Limiter/plugin.py +++ b/plugins/Limiter/plugin.py @@ -32,8 +32,10 @@ from supybot.commands import * import supybot.ircmsgs as ircmsgs import supybot.ircutils as ircutils import supybot.callbacks as callbacks +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Limiter') - +@internationalizeDocstring class Limiter(callbacks.Plugin): """In order to use this plugin, its config values need to be properly setup. supybot.plugins.Limiter.enable needs to be set to True and diff --git a/plugins/Math/config.py b/plugins/Math/config.py index 73e68ffeb..db6474c78 100644 --- a/plugins/Math/config.py +++ b/plugins/Math/config.py @@ -29,6 +29,8 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Math') def configure(advanced): # This will be called by supybot to configure this module. advanced is @@ -42,7 +44,7 @@ def configure(advanced): Math = conf.registerPlugin('Math') # This is where your configuration variables (if any) should go. For example: # conf.registerGlobalValue(Math, 'someConfigVariableName', -# registry.Boolean(False, """Help for someConfigVariableName.""")) +# registry.Boolean(False, _("""Help for someConfigVariableName."""))) # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/Math/messages.pot b/plugins/Math/messages.pot new file mode 100644 index 000000000..9f2932cc0 --- /dev/null +++ b/plugins/Math/messages.pot @@ -0,0 +1,129 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-17 15:35+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: plugin.py:52 +#, docstring +msgid "" +" [] \n" +"\n" +" Converts from base to base .\n" +" If is left out, it converts to decimal.\n" +" " +msgstr "" + +#: plugin.py:63 +msgid "Invalid for base %s: %s" +msgstr "" + +#: plugin.py:69 +#, docstring +msgid "Convert a decimal number to another base; returns a string." +msgstr "" + +#: plugin.py:90 +#, docstring +msgid "" +"Convert a number from any base, 2 through 36, to any other\n" +" base, 2 through 36. Returns a string." +msgstr "" + +#: plugin.py:157 +#, docstring +msgid "" +"\n" +"\n" +" Returns the value of the evaluated . The syntax is\n" +" Python syntax; the type of arithmetic is floating point. Floating\n" +" point arithmetic is used in order to prevent a user from being able to\n" +" crash to the bot with something like '10**10**10**10'. One consequence\n" +" is that large values such as '10**24' might not be exact.\n" +" " +msgstr "" + +#: plugin.py:166 plugin.py:220 +msgid "There's really no reason why you should have underscores or brackets in your mathematical expression. Please remove them." +msgstr "" + +#: plugin.py:172 plugin.py:228 +msgid "You can't use lambda in this command." +msgstr "" + +#: plugin.py:202 plugin.py:236 +msgid "The answer exceeded %s or so." +msgstr "" + +#: plugin.py:204 plugin.py:238 +msgid "Something in there wasn't a valid number." +msgstr "" + +#: plugin.py:206 plugin.py:240 +msgid "%s is not a defined function." +msgstr "" + +#: plugin.py:213 +#, docstring +msgid "" +"\n" +"\n" +" This is the same as the calc command except that it allows integer\n" +" math, and can thus cause the bot to suck up CPU. Hence it requires\n" +" the 'trusted' capability to use.\n" +" " +msgstr "" + +#: plugin.py:250 +#, docstring +msgid "" +"\n" +"\n" +" Returns the value of an RPN expression.\n" +" " +msgstr "" + +#: plugin.py:275 +msgid "Not enough arguments for %s" +msgstr "" + +#: plugin.py:288 +msgid "%q is not a defined function." +msgstr "" + +#: plugin.py:295 +msgid "Stack: [%s]" +msgstr "" + +#: plugin.py:299 +#, docstring +msgid "" +"[] to \n" +"\n" +" Converts from to . If number isn't given, it\n" +" defaults to 1. For unit information, see 'units' command.\n" +" " +msgstr "" + +#: plugin.py:314 +#, docstring +msgid "" +" []\n" +"\n" +" With no arguments, returns a list of measurement types, which can be\n" +" passed as arguments. When called with a type as an argument, returns\n" +" the units of that type.\n" +" " +msgstr "" + diff --git a/plugins/Math/plugin.py b/plugins/Math/plugin.py index d3bb5aeb0..683b9c709 100644 --- a/plugins/Math/plugin.py +++ b/plugins/Math/plugin.py @@ -39,12 +39,15 @@ import string import supybot.utils as utils from supybot.commands import * import supybot.callbacks as callbacks +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Math') convertcore = utils.python.universalImport('local.convertcore') baseArg = ('int', 'base', lambda i: i <= 36) class Math(callbacks.Plugin): + @internationalizeDocstring def base(self, irc, msg, args, frm, to, number): """ [] @@ -57,7 +60,7 @@ class Math(callbacks.Plugin): try: irc.reply(self._convertBaseToBase(number, to, frm)) except ValueError: - irc.error('Invalid for base %s: %s' % (frm, number)) + irc.error(_('Invalid for base %s: %s') % (frm, number)) base = wrap(base, [('int', 'base', lambda i: 2 <= i <= 36), optional(('int', 'base', lambda i: 2 <= i <= 36), 10), additional('something')]) @@ -149,6 +152,7 @@ class Math(callbacks.Plugin): # Then we delete all square brackets, underscores, and whitespace, so no # one can do list comprehensions or call __...__ functions. ### + @internationalizeDocstring def calc(self, irc, msg, args, text): """ @@ -159,13 +163,13 @@ class Math(callbacks.Plugin): is that large values such as '10**24' might not be exact. """ if text != text.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.') + 'expression. Please remove them.')) return #text = text.translate(utils.str.chars, '_[] \t') if 'lambda' in text: - irc.error('You can\'t use lambda in this command.') + irc.error(_('You can\'t use lambda in this command.')) return text = text.lower() def handleMatch(m): @@ -195,15 +199,16 @@ class Math(callbacks.Plugin): irc.reply(self._complexToString(x)) except OverflowError: maxFloat = math.ldexp(0.9999999999999999, 1024) - irc.error('The answer exceeded %s or so.' % maxFloat) + irc.error(_('The answer exceeded %s or so.') % maxFloat) except TypeError: - irc.error('Something in there wasn\'t a valid number.') + irc.error(_('Something in there wasn\'t a valid number.')) except NameError, e: - irc.error('%s is not a defined function.' % str(e).split()[1]) + irc.error(_('%s is not a defined function.') % str(e).split()[1]) except Exception, e: irc.error(str(e)) calc = wrap(calc, ['text']) + @internationalizeDocstring def icalc(self, irc, msg, args, text): """ @@ -212,15 +217,15 @@ class Math(callbacks.Plugin): the 'trusted' capability to use. """ if text != text.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.') + 'expression. Please remove them.')) return # This removes spaces, too, but we'll leave the removal of _[] for # safety's sake. text = text.translate(utils.str.chars, '_[] \t') if 'lambda' in text: - irc.error('You can\'t use lambda in this command.') + irc.error(_('You can\'t use lambda in this command.')) return text = text.replace('lambda', '') try: @@ -228,11 +233,11 @@ class Math(callbacks.Plugin): irc.reply(str(eval(text, self._mathEnv, self._mathEnv))) except OverflowError: maxFloat = math.ldexp(0.9999999999999999, 1024) - irc.error('The answer exceeded %s or so.' % maxFloat) + irc.error(_('The answer exceeded %s or so.') % maxFloat) except TypeError: - irc.error('Something in there wasn\'t a valid number.') + irc.error(_('Something in there wasn\'t a valid number.')) except NameError, e: - irc.error('%s is not a defined function.' % str(e).split()[1]) + irc.error(_('%s is not a defined function.') % str(e).split()[1]) except Exception, e: irc.error(utils.exnToString(e)) icalc = wrap(icalc, [('checkCapability', 'trusted'), 'text']) @@ -267,7 +272,7 @@ class Math(callbacks.Plugin): except TypeError: pass if not called: - irc.error('Not enough arguments for %s' % arg) + irc.error(_('Not enough arguments for %s') % arg) return else: stack.append(f) @@ -280,14 +285,16 @@ class Math(callbacks.Plugin): try: stack.append(eval(s, self._mathEnv, self._mathEnv)) except SyntaxError: - irc.error(format('%q is not a defined function.', arg)) + irc.error(format(_('%q is not a defined function.'), + arg)) return if len(stack) == 1: irc.reply(str(self._complexToString(complex(stack[0])))) else: s = ', '.join(map(self._complexToString, map(complex, stack))) - irc.reply('Stack: [%s]' % s) + irc.reply(_('Stack: [%s]') % s) + @internationalizeDocstring def convert(self, irc, msg, args, number, unit1, unit2): """[] to @@ -302,6 +309,7 @@ class Math(callbacks.Plugin): irc.error(str(ude)) convert = wrap(convert, [optional('float', 1.0),'something','to','text']) + @internationalizeDocstring def units(self, irc, msg, args, type): """ [] diff --git a/plugins/Misc/config.py b/plugins/Misc/config.py index 57c4dada5..f1c4dc19e 100644 --- a/plugins/Misc/config.py +++ b/plugins/Misc/config.py @@ -29,6 +29,8 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Misc') def configure(advanced): # This will be called by supybot to configure this module. advanced is @@ -40,24 +42,24 @@ def configure(advanced): Misc = conf.registerPlugin('Misc') conf.registerGlobalValue(Misc, 'listPrivatePlugins', - registry.Boolean(True, """Determines whether the bot will list private + registry.Boolean(True, _("""Determines whether the bot will list private plugins with the list command if given the --private switch. If this is disabled, non-owner users should be unable to see what private plugins - are loaded.""")) + are loaded."""))) conf.registerGlobalValue(Misc, 'timestampFormat', - registry.String('[%H:%M:%S]', """Determines the format string for + registry.String('[%H:%M:%S]', _("""Determines the format string for timestamps in the Misc.last command. Refer to the Python documentation for the time module to see what formats are accepted. If you set this - variable to the empty string, the timestamp will not be shown.""")) + variable to the empty string, the timestamp will not be shown."""))) conf.registerGroup(Misc, 'last') conf.registerGroup(Misc.last, 'nested') conf.registerChannelValue(Misc.last.nested, - 'includeTimestamp', registry.Boolean(False, """Determines whether or not + 'includeTimestamp', registry.Boolean(False, _("""Determines whether or not the timestamp will be included in the output of last when it is part of a - nested command""")) + nested command"""))) conf.registerChannelValue(Misc.last.nested, - 'includeNick', registry.Boolean(False, """Determines whether or not the + 'includeNick', registry.Boolean(False, _("""Determines whether or not the nick will be included in the output of last when it is part of a nested - command""")) + command"""))) # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/Misc/messages.pot b/plugins/Misc/messages.pot new file mode 100644 index 000000000..59d3b15a6 --- /dev/null +++ b/plugins/Misc/messages.pot @@ -0,0 +1,230 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-17 15:35+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: config.py:45 +msgid "" +"Determines whether the bot will list private\n" +" plugins with the list command if given the --private switch. If this is\n" +" disabled, non-owner users should be unable to see what private plugins\n" +" are loaded." +msgstr "" + +#: config.py:50 +msgid "" +"Determines the format string for\n" +" timestamps in the Misc.last command. Refer to the Python documentation\n" +" for the time module to see what formats are accepted. If you set this\n" +" variable to the empty string, the timestamp will not be shown." +msgstr "" + +#: config.py:57 +msgid "" +"Determines whether or not\n" +" the timestamp will be included in the output of last when it is part of a\n" +" nested command" +msgstr "" + +#: config.py:61 +msgid "" +"Determines whether or not the\n" +" nick will be included in the output of last when it is part of a nested\n" +" command" +msgstr "" + +#: plugin.py:81 +msgid "You've given me %s invalid commands within the last minute; I'm now ignoring you for %s." +msgstr "" + +#: plugin.py:93 +msgid "The %q plugin is loaded, but there is no command named %q in it. Try \"list %s\" to see the commands in the %q plugin." +msgstr "" + +#: plugin.py:119 +#, docstring +msgid "" +"[--private] []\n" +"\n" +" Lists the commands available in the given plugin. If no plugin is\n" +" given, lists the public plugins available. If --private is given,\n" +" lists the private plugins.\n" +" " +msgstr "" + +#: plugin.py:144 +msgid "There are no private plugins." +msgstr "" + +#: plugin.py:146 +msgid "There are no public plugins." +msgstr "" + +#: plugin.py:153 +msgid "That plugin exists, but has no commands. This probably means that it has some configuration variables that can be changed in order to modify its behavior. Try \"config list supybot.plugins.%s\" to see what configuration variables it has." +msgstr "" + +#: plugin.py:164 +#, docstring +msgid "" +"\n" +"\n" +" Searches for in the commands currently offered by the bot,\n" +" returning a list of the commands containing that string.\n" +" " +msgstr "" + +#: plugin.py:183 +msgid "No appropriate commands were found." +msgstr "" + +#: plugin.py:188 +#, docstring +msgid "" +"[] []\n" +"\n" +" This command gives a useful description of what does.\n" +" is only necessary if the command is in more than one plugin.\n" +" " +msgstr "" + +#: plugin.py:198 +msgid "That command exists in the %L plugins. Please specify exactly which plugin command you want help with." +msgstr "" + +#: plugin.py:205 +msgid "There is no command %q." +msgstr "" + +#: plugin.py:211 +#, docstring +msgid "" +"takes no arguments\n" +"\n" +" Returns the version of the current bot.\n" +" " +msgstr "" + +#: plugin.py:217 +msgid "The newest version available online is %s." +msgstr "" + +#: plugin.py:221 +msgid "I couldn't fetch the newest version from the Supybot website." +msgstr "" + +#: plugin.py:223 +msgid "The current (running) version of this Supybot is %s. %s" +msgstr "" + +#: plugin.py:230 +#, docstring +msgid "" +"takes no arguments\n" +"\n" +" Returns a URL saying where to get Supybot.\n" +" " +msgstr "" + +#: plugin.py:234 +msgid "My source is at http://supybot.com/" +msgstr "" + +#: plugin.py:239 +#, docstring +msgid "" +"[]\n" +"\n" +" If the last command was truncated due to IRC message length\n" +" limitations, returns the next chunk of the result of the last command.\n" +" If is given, it takes the continuation of the last command from\n" +" instead of the person sending this message.\n" +" " +msgstr "" + +#: plugin.py:253 +msgid "%s has no public mores." +msgstr "" + +#: plugin.py:256 +msgid "Sorry, I can't find any mores for %s" +msgstr "" + +#: plugin.py:265 +msgid "You haven't asked me a command; perhaps you want to see someone else's more. To do so, call this command with that person's nick." +msgstr "" + +#: plugin.py:269 +msgid "That's all, there is no more." +msgstr "" + +#: plugin.py:279 +#, docstring +msgid "" +"[--{from,in,on,with,without,regexp} ] [--nolimit]\n" +"\n" +" Returns the last message matching the given criteria. --from requires\n" +" a nick from whom the message came; --in requires a channel the message\n" +" was sent to; --on requires a network the message was sent on; --with\n" +" requires some string that had to be in the message; --regexp requires\n" +" a regular expression the message must match; --nolimit returns all\n" +" the messages that can be found. By default, the channel this command is\n" +" given in is searched.\n" +" " +msgstr "" + +#: plugin.py:373 +msgid "I couldn't find a message matching that criteria in my history of %s messages." +msgstr "" + +#: plugin.py:388 +#, docstring +msgid "" +" \n" +"\n" +" Tells the whatever is. Use nested commands to your\n" +" benefit here.\n" +" " +msgstr "" + +#: plugin.py:396 +msgid "Dude, just give the command. No need for the tell." +msgstr "" + +#: plugin.py:401 +msgid "You just told me, why should I tell myself?" +msgstr "" + +#: plugin.py:406 +msgid "I haven't seen %s, I'll let you do the telling." +msgstr "" + +#: plugin.py:411 +msgid "%s wants me to tell you: %s" +msgstr "" + +#: plugin.py:417 +#, docstring +msgid "" +"takes no arguments\n" +"\n" +" Checks to see if the bot is alive.\n" +" " +msgstr "" + +#: plugin.py:421 +msgid "pong" +msgstr "" + diff --git a/plugins/Misc/plugin.py b/plugins/Misc/plugin.py index a1412f32e..8079dc52e 100644 --- a/plugins/Misc/plugin.py +++ b/plugins/Misc/plugin.py @@ -44,6 +44,8 @@ import supybot.ircutils as ircutils import supybot.callbacks as callbacks from supybot.utils.iter import ifilter +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Misc') class Misc(callbacks.Plugin): def __init__(self, irc): @@ -76,8 +78,8 @@ class Misc(callbacks.Plugin): 'permanently.') ircdb.ignores.add(banmask, time.time() + punishment) if conf.supybot.abuse.flood.command.invalid.notify(): - irc.reply('You\'ve given me %s invalid commands within the last ' - 'minute; I\'m now ignoring you for %s.' % + irc.reply(_('You\'ve given me %s invalid commands within the last ' + 'minute; I\'m now ignoring you for %s.') % (maximum, utils.timeElapsed(punishment, seconds=False))) return @@ -88,10 +90,10 @@ class Misc(callbacks.Plugin): cb = irc.getCallback(tokens[0]) if cb: plugin = cb.name() - irc.error(format('The %q plugin is loaded, but there is ' + irc.error(format(_('The %q plugin is loaded, but there is ' 'no command named %q in it. Try "list ' '%s" to see the commands in the %q ' - 'plugin.', plugin, tokens[1], + 'plugin.'), plugin, tokens[1], plugin, plugin)) else: irc.errorInvalid('command', tokens[0], repr=False) @@ -112,6 +114,7 @@ class Misc(callbacks.Plugin): else: pass # Let's just do nothing, I can't think of better. + @internationalizeDocstring def list(self, irc, msg, args, optlist, cb): """[--private] [] @@ -138,24 +141,25 @@ class Misc(callbacks.Plugin): irc.reply(format('%L', names)) else: if private: - irc.reply('There are no private plugins.') + irc.reply(_('There are no private plugins.')) else: - irc.reply('There are no public plugins.') + irc.reply(_('There are no public plugins.')) else: commands = cb.listCommands() if commands: commands.sort() irc.reply(format('%L', commands)) else: - irc.reply(format('That plugin exists, but has no commands. ' + irc.reply(format(_('That plugin exists, but has no commands. ' 'This probably means that it has some ' 'configuration variables that can be ' 'changed in order to modify its behavior. ' 'Try "config list supybot.plugins.%s" to see ' - 'what configuration variables it has.', + 'what configuration variables it has.'), cb.name())) list = wrap(list, [getopts({'private':''}), additional('plugin')]) + @internationalizeDocstring def apropos(self, irc, msg, args, s): """ @@ -176,9 +180,10 @@ class Misc(callbacks.Plugin): L.sort() irc.reply(format('%L', L)) else: - irc.reply('No appropriate commands were found.') + irc.reply(_('No appropriate commands were found.')) apropos = wrap(apropos, ['lowered']) + @internationalizeDocstring def help(self, irc, msg, args, command): """[] [] @@ -190,17 +195,18 @@ class Misc(callbacks.Plugin): if maxL == command: if len(cbs) > 1: names = sorted([cb.name() for cb in cbs]) - irc.error(format('That command exists in the %L plugins. ' + irc.error(format(_('That command exists in the %L plugins. ' 'Please specify exactly which plugin command ' - 'you want help with.', names)) + 'you want help with.'), names)) else: assert cbs, 'Odd, maxL == command, but no cbs.' irc.reply(cbs[0].getCommandHelp(command, False)) else: - irc.error(format('There is no command %q.', + irc.error(format(_('There is no command %q.'), callbacks.formatCommand(command))) help = wrap(help, [many('something')]) + @internationalizeDocstring def version(self, irc, msg, args): """takes no arguments @@ -208,24 +214,27 @@ class Misc(callbacks.Plugin): """ try: newest = utils.web.getUrl('http://supybot.sf.net/version.txt') - newest ='The newest version available online is %s.'%newest.strip() + newest = _('The newest version available online is %s.') % \ + newest.strip() except utils.web.Error, e: self.log.info('Couldn\'t get website version: %s', e) - newest = 'I couldn\'t fetch the newest version ' \ - 'from the Supybot website.' - s = 'The current (running) version of this Supybot is %s. %s' % \ + newest = _('I couldn\'t fetch the newest version ' + 'from the Supybot website.') + s = _('The current (running) version of this Supybot is %s. %s') % \ (conf.version, newest) irc.reply(s) version = wrap(thread(version)) + @internationalizeDocstring def source(self, irc, msg, args): """takes no arguments Returns a URL saying where to get Supybot. """ - irc.reply('My source is at http://supybot.com/') + irc.reply(_('My source is at http://supybot.com/')) source = wrap(source) + @internationalizeDocstring def more(self, irc, msg, args, nick): """[] @@ -241,10 +250,10 @@ class Misc(callbacks.Plugin): if not private: irc._mores[userHostmask] = L[:] else: - irc.error('%s has no public mores.' % nick) + irc.error(_('%s has no public mores.') % nick) return except KeyError: - irc.error('Sorry, I can\'t find any mores for %s' % nick) + irc.error(_('Sorry, I can\'t find any mores for %s') % nick) return try: L = irc._mores[userHostmask] @@ -253,11 +262,11 @@ class Misc(callbacks.Plugin): chunk += format(' \x02(%n)\x0F', (len(L), 'more', 'message')) irc.reply(chunk, True) except KeyError: - irc.error('You haven\'t asked me a command; perhaps you want ' + irc.error(_('You haven\'t asked me a command; perhaps you want ' 'to see someone else\'s more. To do so, call this ' - 'command with that person\'s nick.') + 'command with that person\'s nick.')) except IndexError: - irc.error('That\'s all, there is no more.') + irc.error(_('That\'s all, there is no more.')) more = wrap(more, [additional('seenNick')]) def _validLastMsg(self, msg): @@ -265,6 +274,7 @@ class Misc(callbacks.Plugin): msg.command == 'PRIVMSG' and \ ircutils.isChannel(msg.args[0]) + @internationalizeDocstring def last(self, irc, msg, args, optlist): """[--{from,in,on,with,without,regexp} ] [--nolimit] @@ -360,8 +370,8 @@ class Misc(callbacks.Plugin): showNick=showNick)) return if not resp: - irc.error('I couldn\'t find a message matching that criteria in ' - 'my history of %s messages.' % len(irc.state.history)) + irc.error(_('I couldn\'t find a message matching that criteria in ' + 'my history of %s messages.') % len(irc.state.history)) else: irc.reply(format('%L', resp)) last = wrap(last, [getopts({'nolimit': '', @@ -373,6 +383,7 @@ class Misc(callbacks.Plugin): 'regexp': 'regexpMatcher',})]) + @internationalizeDocstring def tell(self, irc, msg, args, target, text): """ @@ -382,30 +393,32 @@ class Misc(callbacks.Plugin): if target.lower() == 'me': target = msg.nick if ircutils.isChannel(target): - irc.error('Dude, just give the command. No need for the tell.') + irc.error(_('Dude, just give the command. No need for the tell.')) return if not ircutils.isNick(target): irc.errorInvalid('nick', target) if ircutils.nickEqual(target, irc.nick): - irc.error('You just told me, why should I tell myself?',Raise=True) + irc.error(_('You just told me, why should I tell myself?'), + Raise=True) if target not in irc.state.nicksToHostmasks and \ not ircdb.checkCapability(msg.prefix, 'owner'): # We'll let owners do this. - s = 'I haven\'t seen %s, I\'ll let you do the telling.' % target + s = _('I haven\'t seen %s, I\'ll let you do the telling.') % target irc.error(s, Raise=True) if irc.action: irc.action = False text = '* %s %s' % (irc.nick, text) - s = '%s wants me to tell you: %s' % (msg.nick, text) + s = _('%s wants me to tell you: %s') % (msg.nick, text) irc.reply(s, to=target, private=True) tell = wrap(tell, ['something', 'text']) + @internationalizeDocstring def ping(self, irc, msg, args): """takes no arguments Checks to see if the bot is alive. """ - irc.reply('pong', prefixNick=False) + irc.reply(_('pong'), prefixNick=False) Class = Misc From 45de0926521d9d4ebf67f386c7cc82064a8e8066 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sun, 17 Oct 2010 15:59:23 +0200 Subject: [PATCH 028/136] Internationalize Herald, Internet, Karma, Lart, Later, Limiter, Math, and Misc --- plugins/Misc/config.py | 18 +-- plugins/Misc/messages.pot | 230 ++++++++++++++++++++++++++++++++++++++ plugins/Misc/plugin.py | 77 ++++++++----- 3 files changed, 287 insertions(+), 38 deletions(-) create mode 100644 plugins/Misc/messages.pot diff --git a/plugins/Misc/config.py b/plugins/Misc/config.py index 57c4dada5..f1c4dc19e 100644 --- a/plugins/Misc/config.py +++ b/plugins/Misc/config.py @@ -29,6 +29,8 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Misc') def configure(advanced): # This will be called by supybot to configure this module. advanced is @@ -40,24 +42,24 @@ def configure(advanced): Misc = conf.registerPlugin('Misc') conf.registerGlobalValue(Misc, 'listPrivatePlugins', - registry.Boolean(True, """Determines whether the bot will list private + registry.Boolean(True, _("""Determines whether the bot will list private plugins with the list command if given the --private switch. If this is disabled, non-owner users should be unable to see what private plugins - are loaded.""")) + are loaded."""))) conf.registerGlobalValue(Misc, 'timestampFormat', - registry.String('[%H:%M:%S]', """Determines the format string for + registry.String('[%H:%M:%S]', _("""Determines the format string for timestamps in the Misc.last command. Refer to the Python documentation for the time module to see what formats are accepted. If you set this - variable to the empty string, the timestamp will not be shown.""")) + variable to the empty string, the timestamp will not be shown."""))) conf.registerGroup(Misc, 'last') conf.registerGroup(Misc.last, 'nested') conf.registerChannelValue(Misc.last.nested, - 'includeTimestamp', registry.Boolean(False, """Determines whether or not + 'includeTimestamp', registry.Boolean(False, _("""Determines whether or not the timestamp will be included in the output of last when it is part of a - nested command""")) + nested command"""))) conf.registerChannelValue(Misc.last.nested, - 'includeNick', registry.Boolean(False, """Determines whether or not the + 'includeNick', registry.Boolean(False, _("""Determines whether or not the nick will be included in the output of last when it is part of a nested - command""")) + command"""))) # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/Misc/messages.pot b/plugins/Misc/messages.pot new file mode 100644 index 000000000..59d3b15a6 --- /dev/null +++ b/plugins/Misc/messages.pot @@ -0,0 +1,230 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-17 15:35+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: config.py:45 +msgid "" +"Determines whether the bot will list private\n" +" plugins with the list command if given the --private switch. If this is\n" +" disabled, non-owner users should be unable to see what private plugins\n" +" are loaded." +msgstr "" + +#: config.py:50 +msgid "" +"Determines the format string for\n" +" timestamps in the Misc.last command. Refer to the Python documentation\n" +" for the time module to see what formats are accepted. If you set this\n" +" variable to the empty string, the timestamp will not be shown." +msgstr "" + +#: config.py:57 +msgid "" +"Determines whether or not\n" +" the timestamp will be included in the output of last when it is part of a\n" +" nested command" +msgstr "" + +#: config.py:61 +msgid "" +"Determines whether or not the\n" +" nick will be included in the output of last when it is part of a nested\n" +" command" +msgstr "" + +#: plugin.py:81 +msgid "You've given me %s invalid commands within the last minute; I'm now ignoring you for %s." +msgstr "" + +#: plugin.py:93 +msgid "The %q plugin is loaded, but there is no command named %q in it. Try \"list %s\" to see the commands in the %q plugin." +msgstr "" + +#: plugin.py:119 +#, docstring +msgid "" +"[--private] []\n" +"\n" +" Lists the commands available in the given plugin. If no plugin is\n" +" given, lists the public plugins available. If --private is given,\n" +" lists the private plugins.\n" +" " +msgstr "" + +#: plugin.py:144 +msgid "There are no private plugins." +msgstr "" + +#: plugin.py:146 +msgid "There are no public plugins." +msgstr "" + +#: plugin.py:153 +msgid "That plugin exists, but has no commands. This probably means that it has some configuration variables that can be changed in order to modify its behavior. Try \"config list supybot.plugins.%s\" to see what configuration variables it has." +msgstr "" + +#: plugin.py:164 +#, docstring +msgid "" +"\n" +"\n" +" Searches for in the commands currently offered by the bot,\n" +" returning a list of the commands containing that string.\n" +" " +msgstr "" + +#: plugin.py:183 +msgid "No appropriate commands were found." +msgstr "" + +#: plugin.py:188 +#, docstring +msgid "" +"[] []\n" +"\n" +" This command gives a useful description of what does.\n" +" is only necessary if the command is in more than one plugin.\n" +" " +msgstr "" + +#: plugin.py:198 +msgid "That command exists in the %L plugins. Please specify exactly which plugin command you want help with." +msgstr "" + +#: plugin.py:205 +msgid "There is no command %q." +msgstr "" + +#: plugin.py:211 +#, docstring +msgid "" +"takes no arguments\n" +"\n" +" Returns the version of the current bot.\n" +" " +msgstr "" + +#: plugin.py:217 +msgid "The newest version available online is %s." +msgstr "" + +#: plugin.py:221 +msgid "I couldn't fetch the newest version from the Supybot website." +msgstr "" + +#: plugin.py:223 +msgid "The current (running) version of this Supybot is %s. %s" +msgstr "" + +#: plugin.py:230 +#, docstring +msgid "" +"takes no arguments\n" +"\n" +" Returns a URL saying where to get Supybot.\n" +" " +msgstr "" + +#: plugin.py:234 +msgid "My source is at http://supybot.com/" +msgstr "" + +#: plugin.py:239 +#, docstring +msgid "" +"[]\n" +"\n" +" If the last command was truncated due to IRC message length\n" +" limitations, returns the next chunk of the result of the last command.\n" +" If is given, it takes the continuation of the last command from\n" +" instead of the person sending this message.\n" +" " +msgstr "" + +#: plugin.py:253 +msgid "%s has no public mores." +msgstr "" + +#: plugin.py:256 +msgid "Sorry, I can't find any mores for %s" +msgstr "" + +#: plugin.py:265 +msgid "You haven't asked me a command; perhaps you want to see someone else's more. To do so, call this command with that person's nick." +msgstr "" + +#: plugin.py:269 +msgid "That's all, there is no more." +msgstr "" + +#: plugin.py:279 +#, docstring +msgid "" +"[--{from,in,on,with,without,regexp} ] [--nolimit]\n" +"\n" +" Returns the last message matching the given criteria. --from requires\n" +" a nick from whom the message came; --in requires a channel the message\n" +" was sent to; --on requires a network the message was sent on; --with\n" +" requires some string that had to be in the message; --regexp requires\n" +" a regular expression the message must match; --nolimit returns all\n" +" the messages that can be found. By default, the channel this command is\n" +" given in is searched.\n" +" " +msgstr "" + +#: plugin.py:373 +msgid "I couldn't find a message matching that criteria in my history of %s messages." +msgstr "" + +#: plugin.py:388 +#, docstring +msgid "" +" \n" +"\n" +" Tells the whatever is. Use nested commands to your\n" +" benefit here.\n" +" " +msgstr "" + +#: plugin.py:396 +msgid "Dude, just give the command. No need for the tell." +msgstr "" + +#: plugin.py:401 +msgid "You just told me, why should I tell myself?" +msgstr "" + +#: plugin.py:406 +msgid "I haven't seen %s, I'll let you do the telling." +msgstr "" + +#: plugin.py:411 +msgid "%s wants me to tell you: %s" +msgstr "" + +#: plugin.py:417 +#, docstring +msgid "" +"takes no arguments\n" +"\n" +" Checks to see if the bot is alive.\n" +" " +msgstr "" + +#: plugin.py:421 +msgid "pong" +msgstr "" + diff --git a/plugins/Misc/plugin.py b/plugins/Misc/plugin.py index a1412f32e..f03d26750 100644 --- a/plugins/Misc/plugin.py +++ b/plugins/Misc/plugin.py @@ -44,6 +44,8 @@ import supybot.ircutils as ircutils import supybot.callbacks as callbacks from supybot.utils.iter import ifilter +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Misc') class Misc(callbacks.Plugin): def __init__(self, irc): @@ -76,8 +78,8 @@ class Misc(callbacks.Plugin): 'permanently.') ircdb.ignores.add(banmask, time.time() + punishment) if conf.supybot.abuse.flood.command.invalid.notify(): - irc.reply('You\'ve given me %s invalid commands within the last ' - 'minute; I\'m now ignoring you for %s.' % + irc.reply(_('You\'ve given me %s invalid commands within the last ' + 'minute; I\'m now ignoring you for %s.') % (maximum, utils.timeElapsed(punishment, seconds=False))) return @@ -88,10 +90,10 @@ class Misc(callbacks.Plugin): cb = irc.getCallback(tokens[0]) if cb: plugin = cb.name() - irc.error(format('The %q plugin is loaded, but there is ' + irc.error(format(_('The %q plugin is loaded, but there is ' 'no command named %q in it. Try "list ' '%s" to see the commands in the %q ' - 'plugin.', plugin, tokens[1], + 'plugin.'), plugin, tokens[1], plugin, plugin)) else: irc.errorInvalid('command', tokens[0], repr=False) @@ -112,6 +114,7 @@ class Misc(callbacks.Plugin): else: pass # Let's just do nothing, I can't think of better. + @internationalizeDocstring def list(self, irc, msg, args, optlist, cb): """[--private] [] @@ -138,24 +141,25 @@ class Misc(callbacks.Plugin): irc.reply(format('%L', names)) else: if private: - irc.reply('There are no private plugins.') + irc.reply(_('There are no private plugins.')) else: - irc.reply('There are no public plugins.') + irc.reply(_('There are no public plugins.')) else: commands = cb.listCommands() if commands: commands.sort() irc.reply(format('%L', commands)) else: - irc.reply(format('That plugin exists, but has no commands. ' + irc.reply(format(_('That plugin exists, but has no commands. ' 'This probably means that it has some ' 'configuration variables that can be ' 'changed in order to modify its behavior. ' 'Try "config list supybot.plugins.%s" to see ' - 'what configuration variables it has.', + 'what configuration variables it has.'), cb.name())) list = wrap(list, [getopts({'private':''}), additional('plugin')]) + @internationalizeDocstring def apropos(self, irc, msg, args, s): """ @@ -176,9 +180,10 @@ class Misc(callbacks.Plugin): L.sort() irc.reply(format('%L', L)) else: - irc.reply('No appropriate commands were found.') + irc.reply(_('No appropriate commands were found.')) apropos = wrap(apropos, ['lowered']) + @internationalizeDocstring def help(self, irc, msg, args, command): """[] [] @@ -190,17 +195,18 @@ class Misc(callbacks.Plugin): if maxL == command: if len(cbs) > 1: names = sorted([cb.name() for cb in cbs]) - irc.error(format('That command exists in the %L plugins. ' + irc.error(format(_('That command exists in the %L plugins. ' 'Please specify exactly which plugin command ' - 'you want help with.', names)) + 'you want help with.'), names)) else: assert cbs, 'Odd, maxL == command, but no cbs.' irc.reply(cbs[0].getCommandHelp(command, False)) else: - irc.error(format('There is no command %q.', + irc.error(format(_('There is no command %q.'), callbacks.formatCommand(command))) help = wrap(help, [many('something')]) + @internationalizeDocstring def version(self, irc, msg, args): """takes no arguments @@ -208,24 +214,27 @@ class Misc(callbacks.Plugin): """ try: newest = utils.web.getUrl('http://supybot.sf.net/version.txt') - newest ='The newest version available online is %s.'%newest.strip() + newest = _('The newest version available online is %s.') % \ + newest.strip() except utils.web.Error, e: self.log.info('Couldn\'t get website version: %s', e) - newest = 'I couldn\'t fetch the newest version ' \ - 'from the Supybot website.' - s = 'The current (running) version of this Supybot is %s. %s' % \ + newest = _('I couldn\'t fetch the newest version ' + 'from the Supybot website.') + s = _('The current (running) version of this Supybot is %s. %s') % \ (conf.version, newest) irc.reply(s) version = wrap(thread(version)) + @internationalizeDocstring def source(self, irc, msg, args): """takes no arguments Returns a URL saying where to get Supybot. """ - irc.reply('My source is at http://supybot.com/') + irc.reply(_('My source is at http://supybot.com/')) source = wrap(source) + @internationalizeDocstring def more(self, irc, msg, args, nick): """[] @@ -241,23 +250,27 @@ class Misc(callbacks.Plugin): if not private: irc._mores[userHostmask] = L[:] else: - irc.error('%s has no public mores.' % nick) + irc.error(_('%s has no public mores.') % nick) return except KeyError: - irc.error('Sorry, I can\'t find any mores for %s' % nick) + irc.error(_('Sorry, I can\'t find any mores for %s') % nick) return try: L = irc._mores[userHostmask] chunk = L.pop() if L: - chunk += format(' \x02(%n)\x0F', (len(L), 'more', 'message')) + if len(L) < 2: + more = _('more message') + else: + more = _('more messages') + chunk += format(' \x02(%s)\x0F', more) irc.reply(chunk, True) except KeyError: - irc.error('You haven\'t asked me a command; perhaps you want ' + irc.error(_('You haven\'t asked me a command; perhaps you want ' 'to see someone else\'s more. To do so, call this ' - 'command with that person\'s nick.') + 'command with that person\'s nick.')) except IndexError: - irc.error('That\'s all, there is no more.') + irc.error(_('That\'s all, there is no more.')) more = wrap(more, [additional('seenNick')]) def _validLastMsg(self, msg): @@ -265,6 +278,7 @@ class Misc(callbacks.Plugin): msg.command == 'PRIVMSG' and \ ircutils.isChannel(msg.args[0]) + @internationalizeDocstring def last(self, irc, msg, args, optlist): """[--{from,in,on,with,without,regexp} ] [--nolimit] @@ -360,8 +374,8 @@ class Misc(callbacks.Plugin): showNick=showNick)) return if not resp: - irc.error('I couldn\'t find a message matching that criteria in ' - 'my history of %s messages.' % len(irc.state.history)) + irc.error(_('I couldn\'t find a message matching that criteria in ' + 'my history of %s messages.') % len(irc.state.history)) else: irc.reply(format('%L', resp)) last = wrap(last, [getopts({'nolimit': '', @@ -373,6 +387,7 @@ class Misc(callbacks.Plugin): 'regexp': 'regexpMatcher',})]) + @internationalizeDocstring def tell(self, irc, msg, args, target, text): """ @@ -382,30 +397,32 @@ class Misc(callbacks.Plugin): if target.lower() == 'me': target = msg.nick if ircutils.isChannel(target): - irc.error('Dude, just give the command. No need for the tell.') + irc.error(_('Dude, just give the command. No need for the tell.')) return if not ircutils.isNick(target): irc.errorInvalid('nick', target) if ircutils.nickEqual(target, irc.nick): - irc.error('You just told me, why should I tell myself?',Raise=True) + irc.error(_('You just told me, why should I tell myself?'), + Raise=True) if target not in irc.state.nicksToHostmasks and \ not ircdb.checkCapability(msg.prefix, 'owner'): # We'll let owners do this. - s = 'I haven\'t seen %s, I\'ll let you do the telling.' % target + s = _('I haven\'t seen %s, I\'ll let you do the telling.') % target irc.error(s, Raise=True) if irc.action: irc.action = False text = '* %s %s' % (irc.nick, text) - s = '%s wants me to tell you: %s' % (msg.nick, text) + s = _('%s wants me to tell you: %s') % (msg.nick, text) irc.reply(s, to=target, private=True) tell = wrap(tell, ['something', 'text']) + @internationalizeDocstring def ping(self, irc, msg, args): """takes no arguments Checks to see if the bot is alive. """ - irc.reply('pong', prefixNick=False) + irc.reply(_('pong'), prefixNick=False) Class = Misc From e5e705f78c16f3dcd856a2c89ff8b82c7bec71ae Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sun, 17 Oct 2010 16:02:11 +0200 Subject: [PATCH 029/136] Fix %n issue in Alias --- plugins/Alias/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Alias/plugin.py b/plugins/Alias/plugin.py index d43f22b4f..87ce858b6 100644 --- a/plugins/Alias/plugin.py +++ b/plugins/Alias/plugin.py @@ -163,7 +163,7 @@ def makeNewAlias(name, alias): if biggestDollar and (wildcard or biggestAt): flexargs = _(' at least') doc =format(_('\n\nAlias for %q.'), - flexargs, (biggestDollar, 'argument'), alias) + flexargs, (biggestDollar, _('argument')), alias) f = utils.python.changeFunctionName(f, name, doc) return f From ffc609a299ed2bf3c74c62ceb16dba71053936d9 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sun, 17 Oct 2010 16:59:21 +0200 Subject: [PATCH 030/136] Internationalized MoobotFactoids, News, and NickCapture --- plugins/MoobotFactoids/config.py | 10 +- plugins/MoobotFactoids/messages.pot | 269 ++++++++++++++++++++++++++++ plugins/MoobotFactoids/plugin.py | 82 +++++---- plugins/News/config.py | 4 +- plugins/News/messages.pot | 106 +++++++++++ plugins/News/plugin.py | 31 ++-- plugins/NickCapture/config.py | 10 +- plugins/NickCapture/messages.pot | 42 +++++ plugins/NickCapture/plugin.py | 3 + 9 files changed, 506 insertions(+), 51 deletions(-) create mode 100644 plugins/MoobotFactoids/messages.pot create mode 100644 plugins/News/messages.pot create mode 100644 plugins/NickCapture/messages.pot diff --git a/plugins/MoobotFactoids/config.py b/plugins/MoobotFactoids/config.py index 0595421b3..07f55889c 100644 --- a/plugins/MoobotFactoids/config.py +++ b/plugins/MoobotFactoids/config.py @@ -29,6 +29,8 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('MoobotFactoids') def configure(advanced): # This will be called by supybot to configure this module. advanced is @@ -41,12 +43,12 @@ def configure(advanced): MoobotFactoids = conf.registerPlugin('MoobotFactoids') conf.registerChannelValue(MoobotFactoids, - 'showFactoidIfOnlyOneMatch', registry.Boolean(True, """Determines whether + 'showFactoidIfOnlyOneMatch', registry.Boolean(True, _("""Determines whether or not the factoid value will be shown when a listkeys search returns only - one factoid key.""")) + one factoid key."""))) conf.registerChannelValue(MoobotFactoids, - 'mostCount', registry.Integer(10, """Determines how many items are shown - when the 'most' command is called.""")) + 'mostCount', registry.Integer(10, _("""Determines how many items are shown + when the 'most' command is called."""))) # vim:set shiftwidth=4 softtabstop=8 expandtab textwidth=78 diff --git a/plugins/MoobotFactoids/messages.pot b/plugins/MoobotFactoids/messages.pot new file mode 100644 index 000000000..0c51bc8d8 --- /dev/null +++ b/plugins/MoobotFactoids/messages.pot @@ -0,0 +1,269 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-17 16:36+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: config.py:46 +msgid "" +"Determines whether\n" +" or not the factoid value will be shown when a listkeys search returns only\n" +" one factoid key." +msgstr "" + +#: config.py:50 +msgid "" +"Determines how many items are shown\n" +" when the 'most' command is called." +msgstr "" + +#: plugin.py:293 +#, docstring +msgid "" +"Add the help for \"@help MoobotFactoids\" here (assuming you don't implement a MoobotFactoids\n" +" command). This should describe *how* to use this plugin." +msgstr "" + +#: plugin.py:350 +msgid "%s is %s" +msgstr "" + +#: plugin.py:369 +msgid "Factoid %q is locked." +msgstr "" + +#: plugin.py:376 +msgid "Factoid %q not found." +msgstr "" + +#: plugin.py:386 +msgid "Missing an 'is' or '_is_'." +msgstr "" + +#: plugin.py:402 +msgid "Factoid %q already exists." +msgstr "" + +#: plugin.py:436 +msgid "%s, or %s" +msgstr "" + +#: plugin.py:457 +#, docstring +msgid "" +"[] \n" +"\n" +" Returns the literal factoid for the given factoid key. No parsing of\n" +" the factoid value is done as it is with normal retrieval. \n" +" is only necessary if the message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:470 +#, docstring +msgid "" +"[] \n" +"\n" +" Returns the various bits of info on the factoid for the given key.\n" +" is only necessary if the message isn't sent in the channel\n" +" itself.\n" +" " +msgstr "" + +#: plugin.py:481 plugin.py:521 +msgid "No such factoid: %q" +msgstr "" + +#: plugin.py:490 +msgid "Created by %s on %s." +msgstr "" + +#: plugin.py:496 +msgid " Last modified by %s on %s." +msgstr "" + +#: plugin.py:504 +msgid " Last requested by %s on %s, requested %n." +msgstr "" + +#: plugin.py:511 +msgid " Locked by %s on %s." +msgstr "" + +#: plugin.py:526 +msgid "Factoid %q is already locked." +msgstr "" + +#: plugin.py:529 +msgid "Factoid %q is not locked." +msgstr "" + +#: plugin.py:539 +msgid "Cannot %s someone else's factoid unless you are an admin." +msgstr "" + +#: plugin.py:551 +#, docstring +msgid "" +"[] \n" +"\n" +" Locks the factoid with the given factoid key. Requires that the user\n" +" be registered and have created the factoid originally. is\n" +" only necessary if the message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:562 +#, docstring +msgid "" +"[] \n" +"\n" +" Unlocks the factoid with the given factoid key. Requires that the\n" +" user be registered and have locked the factoid. is only\n" +" necessary if the message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:573 +#, docstring +msgid "" +"[] {popular|authored|recent}\n" +"\n" +" Lists the most {popular|authored|recent} factoids. \"popular\" lists the\n" +" most frequently requested factoids. \"authored\" lists the author with\n" +" the most factoids. \"recent\" lists the most recently created factoids.\n" +" is only necessary if the message isn't sent in the channel\n" +" itself.\n" +" " +msgstr "" + +#: plugin.py:598 +msgid "Most prolific %s: %L" +msgstr "" + +#: plugin.py:600 plugin.py:612 +msgid "There are no factoids in my database." +msgstr "" + +#: plugin.py:607 +msgid "latest factoid" +msgstr "" + +#: plugin.py:609 +msgid "latest factoids" +msgstr "" + +#: plugin.py:610 +msgid "%s: %L" +msgstr "" + +#: plugin.py:619 +msgid "requested factoid" +msgstr "" + +#: plugin.py:621 +msgid "requested factoids" +msgstr "" + +#: plugin.py:622 +msgid "Top %s: %L" +msgstr "" + +#: plugin.py:624 +msgid "No factoids have been requested from my database." +msgstr "" + +#: plugin.py:628 +#, docstring +msgid "" +"[] \n" +"\n" +" Lists the keys of the factoids with the given author. Note that if an\n" +" author has an integer name, you'll have to use that author's id to use\n" +" this function (so don't use integer usernames!). is only\n" +" necessary if the message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:641 +msgid "No factoids by %q found." +msgstr "" + +#: plugin.py:644 +msgid "Author search for %q (%i found): %L" +msgstr "" + +#: plugin.py:651 +#, docstring +msgid "" +"[] \n" +"\n" +" Lists the keys of the factoids whose key contains the provided text.\n" +" is only necessary if the message isn't sent in the channel\n" +" itself.\n" +" " +msgstr "" + +#: plugin.py:659 +msgid "No keys matching %q found." +msgstr "" + +#: plugin.py:666 +msgid "Key search for %q (%i found): %L" +msgstr "" + +#: plugin.py:673 +#, docstring +msgid "" +"[] \n" +"\n" +" Lists the keys of the factoids whose value contains the provided text.\n" +" is only necessary if the message isn't sent in the channel\n" +" itself.\n" +" " +msgstr "" + +#: plugin.py:681 +msgid "No values matching %q found." +msgstr "" + +#: plugin.py:684 +msgid "Value search for %q (%i found): %L" +msgstr "" + +#: plugin.py:691 +#, docstring +msgid "" +"[] \n" +"\n" +" Deletes the factoid with the given key. is only necessary\n" +" if the message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:704 +#, docstring +msgid "" +"[]\n" +"\n" +" Displays a random factoid (along with its key) from the database.\n" +" is only necessary if the message isn't sent in the channel\n" +" itself.\n" +" " +msgstr "" + +#: plugin.py:712 +msgid "No factoids in the database." +msgstr "" + diff --git a/plugins/MoobotFactoids/plugin.py b/plugins/MoobotFactoids/plugin.py index c0818bc77..9a7581a79 100644 --- a/plugins/MoobotFactoids/plugin.py +++ b/plugins/MoobotFactoids/plugin.py @@ -41,6 +41,8 @@ from supybot.commands import * import supybot.plugins as plugins import supybot.ircutils as ircutils import supybot.callbacks as callbacks +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('MoobotFactoids') allchars = string.maketrans('', '') class OptionList(object): @@ -286,6 +288,7 @@ class SqliteMoobotDB(object): MoobotDB = plugins.DB('MoobotFactoids', {'sqlite': SqliteMoobotDB}) +@internationalizeDocstring class MoobotFactoids(callbacks.Plugin): """Add the help for "@help MoobotFactoids" here (assuming you don't implement a MoobotFactoids command). This should describe *how* to use this plugin.""" @@ -344,7 +347,8 @@ class MoobotFactoids(callbacks.Plugin): elif type == 'reply': irc.reply(text, prefixNick=False) elif type == 'define': - irc.reply(format('%s is %s', key, text), prefixNick=False) + irc.reply(format(_('%s is %s'), key, text), + prefixNick=False) else: assert False, 'Spurious type from _parseFactoid' else: @@ -362,14 +366,14 @@ class MoobotFactoids(callbacks.Plugin): def _checkNotLocked(self, irc, channel, key): if self.db.locked(channel, key): - irc.error(format('Factoid %q is locked.', key), Raise=True) + irc.error(format(_('Factoid %q is locked.'), key), Raise=True) def _getFactoid(self, irc, channel, key): fact = self.db.getFactoid(channel, key) if fact is not None: return fact else: - irc.error(format('Factoid %q not found.', key), Raise=True) + irc.error(format(_('Factoid %q not found.'), key), Raise=True) def _getKeyAndFactoid(self, tokens): if '_is_' in tokens: @@ -379,7 +383,7 @@ class MoobotFactoids(callbacks.Plugin): else: self.log.debug('Invalid tokens for {add,replace}Factoid: %s.', tokens) - s = 'Missing an \'is\' or \'_is_\'.' + s = _('Missing an \'is\' or \'_is_\'.') raise ValueError, s (key, newfact) = map(' '.join, utils.iter.split(p, tokens, maxsplit=1)) key = self._sanitizeKey(key) @@ -395,7 +399,7 @@ class MoobotFactoids(callbacks.Plugin): irc.error(str(e), Raise=True) # Check and make sure it's not in the DB already if self.db.getFactoid(channel, key): - irc.error(format('Factoid %q already exists.', key), Raise=True) + irc.error(format(_('Factoid %q already exists.'), key), Raise=True) self.db.addFactoid(channel, key, fact, id) irc.replySuccess() @@ -429,7 +433,7 @@ class MoobotFactoids(callbacks.Plugin): self._checkNotLocked(irc, channel, key) # It's fair game if we get to here fact = fact[0] - new_fact = format('%s, or %s', fact, new_text) + new_fact = format(_('%s, or %s'), fact, new_text) self.db.updateFactoid(channel, key, new_fact, id) irc.replySuccess() @@ -448,6 +452,7 @@ class MoobotFactoids(callbacks.Plugin): self.db.addFactoid(channel, key, fact, id) irc.replySuccess() + @internationalizeDocstring def literal(self, irc, msg, args, channel, key): """[] @@ -460,6 +465,7 @@ class MoobotFactoids(callbacks.Plugin): irc.reply(fact) literal = wrap(literal, ['channeldb', 'text']) + @internationalizeDocstring def factinfo(self, irc, msg, args, channel, key): """[] @@ -472,7 +478,7 @@ class MoobotFactoids(callbacks.Plugin): # Next, get all the info and build the response piece by piece info = self.db.getFactinfo(channel, key) if not info: - irc.error(format('No such factoid: %q', key)) + irc.error(format(_('No such factoid: %q'), key)) return (created_by, created_at, modified_by, modified_at, last_requested_by, last_requested_at, requested_count, locked_by, locked_at) = info @@ -481,27 +487,28 @@ class MoobotFactoids(callbacks.Plugin): created_by = plugins.getUserName(created_by) created_at = time.strftime(conf.supybot.reply.format.time(), time.localtime(int(created_at))) - s += format('Created by %s on %s.', created_by, created_at) + s += format(_('Created by %s on %s.'), created_by, created_at) # Next, modification info, if any. if modified_by is not None: modified_by = plugins.getUserName(modified_by) modified_at = time.strftime(conf.supybot.reply.format.time(), time.localtime(int(modified_at))) - s += format(' Last modified by %s on %s.',modified_by, modified_at) + s += format(_(' Last modified by %s on %s.'), modified_by, + modified_at) # Next, last requested info, if any if last_requested_by is not None: last_by = last_requested_by # not an int user id last_at = time.strftime(conf.supybot.reply.format.time(), time.localtime(int(last_requested_at))) req_count = requested_count - s += format(' Last requested by %s on %s, requested %n.', + s += format(_(' Last requested by %s on %s, requested %n.'), last_by, last_at, (requested_count, 'time')) # Last, locked info if locked_at is not None: lock_at = time.strftime(conf.supybot.reply.format.time(), time.localtime(int(locked_at))) lock_by = plugins.getUserName(locked_by) - s += format(' Locked by %s on %s.', lock_by, lock_at) + s += format(_(' Locked by %s on %s.'), lock_by, lock_at) irc.reply(s) factinfo = wrap(factinfo, ['channeldb', 'text']) @@ -511,15 +518,15 @@ class MoobotFactoids(callbacks.Plugin): id = user.id info = self.db.getFactinfo(channel, key) if not info: - irc.error(format('No such factoid: %q', key)) + irc.error(format(_('No such factoid: %q'), key)) return (created_by, _, _, _, _, _, _, locked_by, _) = info # Don't perform redundant operations if locking and locked_by is not None: - irc.error(format('Factoid %q is already locked.', key)) + irc.error(format(_('Factoid %q is already locked.'), key)) return if not locking and locked_by is None: - irc.error(format('Factoid %q is not locked.', key)) + irc.error(format(_('Factoid %q is not locked.'), key)) return # Can only lock/unlock own factoids unless you're an admin #self.log.debug('admin?: %s', ircdb.checkCapability(id, 'admin')) @@ -529,8 +536,8 @@ class MoobotFactoids(callbacks.Plugin): s = 'lock' else: s = 'unlock' - irc.error(format('Cannot %s someone else\'s factoid unless you ' - 'are an admin.', s)) + irc.error(format(_('Cannot %s someone else\'s factoid unless you ' + 'are an admin.'), s)) return # Okay, we're done, ready to lock/unlock if locking: @@ -539,6 +546,7 @@ class MoobotFactoids(callbacks.Plugin): self.db.unlock(channel, key) irc.replySuccess() + @internationalizeDocstring def lock(self, irc, msg, args, channel, user, key): """[] @@ -549,6 +557,7 @@ class MoobotFactoids(callbacks.Plugin): self._lock(irc, msg, channel, user, key, True) lock = wrap(lock, ['channeldb', 'user', 'text']) + @internationalizeDocstring def unlock(self, irc, msg, args, channel, user, key): """[] @@ -559,6 +568,7 @@ class MoobotFactoids(callbacks.Plugin): self._lock(irc, msg, channel, user, key, False) unlock = wrap(unlock, ['channeldb', 'user', 'text']) + @internationalizeDocstring def most(self, irc, msg, args, channel, method): """[] {popular|authored|recent} @@ -585,27 +595,35 @@ class MoobotFactoids(callbacks.Plugin): author = 'author' if len(L) != 1: author = 'authors' - irc.reply(format('Most prolific %s: %L', author, L)) + irc.reply(format(_('Most prolific %s: %L'), author, L)) else: - irc.error('There are no factoids in my database.') + irc.error(_('There are no factoids in my database.')) def _mostRecent(self, irc, channel, limit): results = self.db.mostRecent(channel, limit) L = [format('%q', t[0]) for t in results] if L: - irc.reply(format('%n: %L', (len(L), 'latest', 'factoid'), L)) + if len(L) < 2: + latest = _('latest factoid') + else: + latest = _('latest factoids') + irc.reply(format(_('%s: %L'), latest, L)) else: - irc.error('There are no factoids in my database.') + irc.error(_('There are no factoids in my database.')) def _mostPopular(self, irc, channel, limit): results = self.db.mostPopular(channel, limit) L = [format('%q (%s)', t[0], t[1]) for t in results] if L: - irc.reply( - format('Top %n: %L', (len(L), 'requested', 'factoid'), L)) + if len(L) < 2: + requested = _('requested factoid') + else: + requested = _('requested factoids') + irc.reply(format(_('Top %s: %L'), requested, L)) else: - irc.error('No factoids have been requested from my database.') + irc.error(_('No factoids have been requested from my database.')) + @internationalizeDocstring def listauth(self, irc, msg, args, channel, author): """[] @@ -620,14 +638,15 @@ class MoobotFactoids(callbacks.Plugin): irc.errorNoUser(name=author, Raise=True) results = self.db.getKeysByAuthor(channel, id) if not results: - irc.reply(format('No factoids by %q found.', author)) + irc.reply(format(_('No factoids by %q found.'), author)) return keys = [format('%q', t[0]) for t in results] - s = format('Author search for %q (%i found): %L', + s = format(_('Author search for %q (%i found): %L'), author, len(keys), keys) irc.reply(s) listauth = wrap(listauth, ['channeldb', 'something']) + @internationalizeDocstring def listkeys(self, irc, msg, args, channel, search): """[] @@ -637,18 +656,19 @@ class MoobotFactoids(callbacks.Plugin): """ results = self.db.getKeysByGlob(channel, search) if not results: - irc.reply(format('No keys matching %q found.', search)) + irc.reply(format(_('No keys matching %q found.'), search)) elif len(results) == 1 and \ self.registryValue('showFactoidIfOnlyOneMatch', channel): key = results[0][0] self.invalidCommand(irc, msg, [key]) else: keys = [format('%q', tup[0]) for tup in results] - s = format('Key search for %q (%i found): %L', + s = format(_('Key search for %q (%i found): %L'), search, len(keys), keys) irc.reply(s) listkeys = wrap(listkeys, ['channeldb', 'text']) + @internationalizeDocstring def listvalues(self, irc, msg, args, channel, search): """[] @@ -658,14 +678,15 @@ class MoobotFactoids(callbacks.Plugin): """ results = self.db.getKeysByValueGlob(channel, search) if not results: - irc.reply(format('No values matching %q found.', search)) + irc.reply(format(_('No values matching %q found.'), search)) return keys = [format('%q', tup[0]) for tup in results] - s = format('Value search for %q (%i found): %L', + s = format(_('Value search for %q (%i found): %L'), search, len(keys), keys) irc.reply(s) listvalues = wrap(listvalues, ['channeldb', 'text']) + @internationalizeDocstring def remove(self, irc, msg, args, channel, _, key): """[] @@ -678,6 +699,7 @@ class MoobotFactoids(callbacks.Plugin): irc.replySuccess() remove = wrap(remove, ['channeldb', 'user', 'text']) + @internationalizeDocstring def random(self, irc, msg, args, channel): """[] @@ -687,7 +709,7 @@ class MoobotFactoids(callbacks.Plugin): """ results = self.db.randomFactoid(channel) if not results: - irc.error('No factoids in the database.') + irc.error(_('No factoids in the database.')) return (fact, key) = results irc.reply(format('Random factoid: %q is %q', key, fact)) diff --git a/plugins/News/config.py b/plugins/News/config.py index 4ba1a6c46..2a2e0e8f4 100644 --- a/plugins/News/config.py +++ b/plugins/News/config.py @@ -29,6 +29,8 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('News') def configure(advanced): # This will be called by supybot to configure this module. advanced is @@ -42,7 +44,7 @@ def configure(advanced): News = conf.registerPlugin('News') # This is where your configuration variables (if any) should go. For example: # conf.registerGlobalValue(News, 'someConfigVariableName', -# registry.Boolean(False, """Help for someConfigVariableName.""")) +# registry.Boolean(False, _("""Help for someConfigVariableName."""))) # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/News/messages.pot b/plugins/News/messages.pot new file mode 100644 index 000000000..9a77314ef --- /dev/null +++ b/plugins/News/messages.pot @@ -0,0 +1,106 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-17 16:53+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: plugin.py:57 +msgid "%s (Subject: %q, added by %s on %s)" +msgstr "" + +#: plugin.py:61 +msgid "%s (Subject: %q, added by %s on %s, expires at %s)" +msgstr "" + +#: plugin.py:120 +#, docstring +msgid "" +"[] : \n" +"\n" +" Adds a given news item of to a channel with the given .\n" +" If isn't 0, that news item will expire seconds from\n" +" now. is only necessary if the message isn't sent in the\n" +" channel itself.\n" +" " +msgstr "" + +#: plugin.py:132 +msgid "(News item #%i added)" +msgstr "" + +#: plugin.py:137 +#, docstring +msgid "" +"[] []\n" +"\n" +" Display the news items for in the format of '(#id) subject'.\n" +" If is given, retrieve only that news item; otherwise retrieve all\n" +" news items. is only necessary if the message isn't sent in\n" +" the channel itself.\n" +" " +msgstr "" + +#: plugin.py:148 +msgid "News for %s: %s" +msgstr "" + +#: plugin.py:151 +msgid "No news for %s." +msgstr "" + +#: plugin.py:157 plugin.py:171 plugin.py:187 plugin.py:203 +msgid "news item id" +msgstr "" + +#: plugin.py:162 +#, docstring +msgid "" +"[] \n" +"\n" +" Removes the news item with from . is only\n" +" necessary if the message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:176 +#, docstring +msgid "" +"[] \n" +"\n" +" Changes the news item with from according to the\n" +" regular expression . should be of the form\n" +" s/text/replacement/flags. is only necessary if the message\n" +" isn't sent on the channel itself.\n" +" " +msgstr "" + +#: plugin.py:192 +#, docstring +msgid "" +"[] []\n" +"\n" +" Returns the old news item for with . If no number is\n" +" given, returns all the old news items in reverse order. is\n" +" only necessary if the message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:208 +msgid "Old news for %s: %s" +msgstr "" + +#: plugin.py:211 +msgid "No old news for %s." +msgstr "" + diff --git a/plugins/News/plugin.py b/plugins/News/plugin.py index 554ddd825..5a9d5d95d 100644 --- a/plugins/News/plugin.py +++ b/plugins/News/plugin.py @@ -36,6 +36,8 @@ from supybot.commands import * import supybot.plugins as plugins import supybot.ircutils as ircutils import supybot.callbacks as callbacks +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('News') class DbiNewsDB(plugins.DbiChannelDB): @@ -52,12 +54,12 @@ class DbiNewsDB(plugins.DbiChannelDB): def __str__(self): user = plugins.getUserName(self.by) if self.expires == 0: - s = format('%s (Subject: %q, added by %s on %s)', + s = format(_('%s (Subject: %q, added by %s on %s)'), self.text, self.subject, self.by, utils.str.timestamp(self.at)) else: - s = format('%s (Subject: %q, added by %s on %s, ' - 'expires at %s)', + s = format(_('%s (Subject: %q, added by %s on %s, ' + 'expires at %s)'), self.text, self.subject, user, utils.str.timestamp(self.at), utils.str.timestamp(self.expires)) @@ -113,6 +115,7 @@ class News(callbacks.Plugin): self.__parent.die() self.db.close() + @internationalizeDocstring def add(self, irc, msg, args, channel, user, at, expires, news): """[] : @@ -126,9 +129,10 @@ class News(callbacks.Plugin): except ValueError: raise callbacks.ArgumentError id = self.db.add(channel, subject, text, at, expires, user.id) - irc.replySuccess(format('(News item #%i added)', id)) + irc.replySuccess(format(_('(News item #%i added)'), id)) add = wrap(add, ['channeldb', 'user', 'now', 'expiry', 'text']) + @internationalizeDocstring def news(self, irc, msg, args, channel, id): """[] [] @@ -141,18 +145,19 @@ class News(callbacks.Plugin): try: records = self.db.get(channel) items = [format('(#%i) %s', R.id, R.subject) for R in records] - s = format('News for %s: %s', channel, '; '.join(items)) + s = format(_('News for %s: %s'), channel, '; '.join(items)) irc.reply(s) except dbi.NoRecordError: - irc.reply(format('No news for %s.', channel)) + irc.reply(format(_('No news for %s.'), channel)) else: try: record = self.db.get(channel, id) irc.reply(str(record)) except dbi.NoRecordError, id: - irc.errorInvalid('news item id', id) + irc.errorInvalid(_('news item id'), id) news = wrap(news, ['channeldb', additional('positiveInt')]) + @internationalizeDocstring def remove(self, irc, msg, args, channel, id): """[] @@ -163,9 +168,10 @@ class News(callbacks.Plugin): self.db.remove(channel, id) irc.replySuccess() except dbi.NoRecordError: - irc.errorInvalid('news item id', id) + irc.errorInvalid(_('news item id'), id) remove = wrap(remove, ['channeldb', 'positiveInt']) + @internationalizeDocstring def change(self, irc, msg, args, channel, id, replacer): """[] @@ -178,9 +184,10 @@ class News(callbacks.Plugin): self.db.change(channel, id, replacer) irc.replySuccess() except dbi.NoRecordError: - irc.errorInvalid('news item id', id) + irc.errorInvalid(_('news item id'), id) change = wrap(change, ['channeldb', 'positiveInt', 'regexpReplacer']) + @internationalizeDocstring def old(self, irc, msg, args, channel, id): """[] [] @@ -193,15 +200,15 @@ class News(callbacks.Plugin): record = self.db.getOld(channel, id) irc.reply(str(record)) except dbi.NoRecordError, id: - irc.errorInvalid('news item id', id) + irc.errorInvalid(_('news item id'), id) else: try: records = self.db.getOld(channel) items = [format('(#%i) %s', R.id, R.subject) for R in records] - s = format('Old news for %s: %s', channel, '; '.join(items)) + s = format(_('Old news for %s: %s'), channel, '; '.join(items)) irc.reply(s) except dbi.NoRecordError: - irc.reply(format('No old news for %s.', channel)) + irc.reply(format(_('No old news for %s.'), channel)) old = wrap(old, ['channeldb', additional('positiveInt')]) diff --git a/plugins/NickCapture/config.py b/plugins/NickCapture/config.py index bad856b12..a333679a3 100644 --- a/plugins/NickCapture/config.py +++ b/plugins/NickCapture/config.py @@ -29,6 +29,8 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('NickCapture') def configure(advanced): # This will be called by supybot to configure this module. advanced is @@ -42,11 +44,11 @@ def configure(advanced): NickCapture = conf.registerPlugin('NickCapture') conf.registerPlugin('NickCapture') conf.registerGlobalValue(NickCapture, 'ison', - registry.Boolean(True, """Determines whether the bot will check - occasionally if its preferred nick is in use via the ISON command.""")) + registry.Boolean(True, _("""Determines whether the bot will check + occasionally if its preferred nick is in use via the ISON command."""))) conf.registerGlobalValue(NickCapture.ison, 'period', - registry.PositiveInteger(600, """Determines how often (in seconds) the bot - will check whether its nick ISON.""")) + registry.PositiveInteger(600, _("""Determines how often (in seconds) the bot + will check whether its nick ISON."""))) # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/NickCapture/messages.pot b/plugins/NickCapture/messages.pot new file mode 100644 index 000000000..46813d8b7 --- /dev/null +++ b/plugins/NickCapture/messages.pot @@ -0,0 +1,42 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-17 16:58+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: config.py:47 +msgid "" +"Determines whether the bot will check\n" +" occasionally if its preferred nick is in use via the ISON command." +msgstr "" + +#: config.py:50 +msgid "" +"Determines how often (in seconds) the bot\n" +" will check whether its nick ISON." +msgstr "" + +#: plugin.py:41 +#, docstring +msgid "" +"This module constantly tries to take whatever nick is configured as\n" +" supybot.nick. Just make sure that's set appropriately, and thus plugin\n" +" will do the rest." +msgstr "" + +#: plugin.py:90 +#, docstring +msgid "This is returned by the ISON command." +msgstr "" + diff --git a/plugins/NickCapture/plugin.py b/plugins/NickCapture/plugin.py index e81ad5718..2d7e7602d 100644 --- a/plugins/NickCapture/plugin.py +++ b/plugins/NickCapture/plugin.py @@ -33,7 +33,10 @@ import supybot.conf as conf import supybot.ircmsgs as ircmsgs import supybot.ircutils as ircutils import supybot.callbacks as callbacks +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('NickCapture') +@internationalizeDocstring class NickCapture(callbacks.Plugin): """This module constantly tries to take whatever nick is configured as supybot.nick. Just make sure that's set appropriately, and thus plugin From 029c0cbe806701151e575dcd010d7e8faa3a13ae Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sun, 17 Oct 2010 18:37:13 +0200 Subject: [PATCH 031/136] Internationalize Nickometer, Praise, Protector, Quote, and QuoteGrabs --- plugins/Nickometer/config.py | 4 +- plugins/Nickometer/messages.pot | 31 ++++++ plugins/Nickometer/plugin.py | 5 +- plugins/Praise/config.py | 8 +- plugins/Praise/messages.pot | 60 +++++++++++ plugins/Praise/plugin.py | 14 ++- plugins/Protector/config.py | 10 +- plugins/Protector/messages.pot | 31 ++++++ plugins/Protector/plugin.py | 2 + plugins/Quote/config.py | 4 +- plugins/Quote/messages.pot | 31 ++++++ plugins/Quote/plugin.py | 5 +- plugins/QuoteGrabs/config.py | 20 ++-- plugins/QuoteGrabs/messages.pot | 170 ++++++++++++++++++++++++++++++++ plugins/QuoteGrabs/plugin.py | 35 ++++--- 15 files changed, 392 insertions(+), 38 deletions(-) create mode 100644 plugins/Nickometer/messages.pot create mode 100644 plugins/Praise/messages.pot create mode 100644 plugins/Protector/messages.pot create mode 100644 plugins/Quote/messages.pot create mode 100644 plugins/QuoteGrabs/messages.pot diff --git a/plugins/Nickometer/config.py b/plugins/Nickometer/config.py index 27cc2568f..c8945d41f 100644 --- a/plugins/Nickometer/config.py +++ b/plugins/Nickometer/config.py @@ -30,6 +30,8 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Nickometer') def configure(advanced): # This will be called by setup.py to configure this module. Advanced is @@ -42,7 +44,7 @@ def configure(advanced): Nickometer = conf.registerPlugin('Nickometer') # This is where your configuration variables (if any) should go. For example: # conf.registerGlobalValue(Nickometer, 'someConfigVariableName', -# registry.Boolean(False, """Help for someConfigVariableName.""")) +# registry.Boolean(False, _("""Help for someConfigVariableName."""))) # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/Nickometer/messages.pot b/plugins/Nickometer/messages.pot new file mode 100644 index 000000000..fd8dfe20c --- /dev/null +++ b/plugins/Nickometer/messages.pot @@ -0,0 +1,31 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-17 18:28+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: plugin.py:84 +#, docstring +msgid "" +"[]\n" +"\n" +" Tells you how lame said nick is. If is not given, uses the\n" +" nick of the person giving the command.\n" +" " +msgstr "" + +#: plugin.py:226 +msgid "The \"lame nick-o-meter\" reading for \"%s\" is %s%%." +msgstr "" + diff --git a/plugins/Nickometer/plugin.py b/plugins/Nickometer/plugin.py index ec24737ff..b5bac6d2c 100644 --- a/plugins/Nickometer/plugin.py +++ b/plugins/Nickometer/plugin.py @@ -53,6 +53,8 @@ import string import supybot.callbacks as callbacks from supybot.commands import wrap, additional +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Nickometer') def slowExponent(x): return 1.3 * x * (1 - math.atan(x / 6.0) * 2 / math.pi) @@ -77,6 +79,7 @@ class Nickometer(callbacks.Plugin): self.log.debug('%s lameness points awarded: %s', damage, reason) return damage + @internationalizeDocstring def nickometer(self, irc, msg, args, nick): """[] @@ -220,7 +223,7 @@ class Nickometer(callbacks.Plugin): # if it's above 99.9%, show as many digits as is interesting score_string=re.sub('(99\\.9*\\d|\\.\\d).*','\\1',`percentage`) - irc.reply('The "lame nick-o-meter" reading for "%s" is %s%%.' % + irc.reply(_('The "lame nick-o-meter" reading for "%s" is %s%%.') % (originalNick, score_string)) self.log.debug('Calculated lameness score for %s as %s ' diff --git a/plugins/Praise/config.py b/plugins/Praise/config.py index b16b5d842..fc5aadd62 100644 --- a/plugins/Praise/config.py +++ b/plugins/Praise/config.py @@ -29,6 +29,8 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Praise') def configure(advanced): # This will be called by supybot to configure this module. advanced is @@ -42,10 +44,10 @@ def configure(advanced): Praise = conf.registerPlugin('Praise') # This is where your configuration variables (if any) should go. For example: # conf.registerGlobalValue(Praise, 'someConfigVariableName', -# registry.Boolean(False, """Help for someConfigVariableName.""")) +# registry.Boolean(False, _("""Help for someConfigVariableName."""))) conf.registerChannelValue(Praise, 'showIds', - registry.Boolean(False, """Determines whether the bot will show the ids of - a praise when the praise is given.""")) + registry.Boolean(False, _("""Determines whether the bot will show the ids of + a praise when the praise is given."""))) # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/Praise/messages.pot b/plugins/Praise/messages.pot new file mode 100644 index 000000000..81a2b8502 --- /dev/null +++ b/plugins/Praise/messages.pot @@ -0,0 +1,60 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-17 18:33+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: config.py:49 +msgid "" +"Determines whether the bot will show the ids of\n" +" a praise when the praise is given." +msgstr "" + +#: plugin.py:40 +#, docstring +msgid "" +"Praise is a plugin for ... well, praising things. Feel free to add\n" +" your own flavor to it by customizing what praises it gives. Use \"praise\n" +" add \" to add new ones, making sure to include \"$who\" in where\n" +" you want to insert the thing being praised.\n" +" " +msgstr "" + +#: plugin.py:54 +msgid "Praises must contain $who." +msgstr "" + +#: plugin.py:58 +#, docstring +msgid "" +"[] [] [for ]\n" +"\n" +" Praises (for , if given). If is given, uses\n" +" that specific praise. is only necessary if the message isn't\n" +" sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:74 +msgid "There is no praise with id #%i." +msgstr "" + +#: plugin.py:79 +msgid "There are no praises in my database for %s." +msgstr "" + +#: plugin.py:87 +msgid " for " +msgstr "" + diff --git a/plugins/Praise/plugin.py b/plugins/Praise/plugin.py index dd6ad4171..2d912e8c5 100644 --- a/plugins/Praise/plugin.py +++ b/plugins/Praise/plugin.py @@ -32,7 +32,10 @@ import re from supybot.commands import * import supybot.plugins as plugins import supybot.ircutils as ircutils +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Praise') +@internationalizeDocstring class Praise(plugins.ChannelIdDatabasePlugin): """Praise is a plugin for ... well, praising things. Feel free to add your own flavor to it by customizing what praises it gives. Use "praise @@ -48,8 +51,9 @@ class Praise(plugins.ChannelIdDatabasePlugin): def addValidator(self, irc, text): if '$who' not in text: - irc.error('Praises must contain $who.', Raise=True) + irc.error(_('Praises must contain $who.'), Raise=True) + @internationalizeDocstring def praise(self, irc, msg, args, channel, id, text): """[] [] [for ] @@ -67,20 +71,20 @@ class Praise(plugins.ChannelIdDatabasePlugin): try: praise = self.db.get(channel, id) except KeyError: - irc.error(format('There is no praise with id #%i.', id)) + irc.error(format(_('There is no praise with id #%i.'), id)) return else: praise = self.db.random(channel) if not praise: - irc.error(format('There are no praises in my database ' \ - 'for %s.', channel)) + irc.error(format(_('There are no praises in my database ' \ + 'for %s.'), channel)) return text = self._replaceFirstPerson(praise.text, msg.nick) reason = self._replaceFirstPerson(reason, msg.nick) target = self._replaceFirstPerson(target, msg.nick) text = text.replace('$who', target) if reason: - text += ' for ' + reason + text += _(' for ') + reason if self.registryValue('showIds', channel): text += format(' (#%i)', praise.id) irc.reply(text, action=True) diff --git a/plugins/Protector/config.py b/plugins/Protector/config.py index 977bd0d7a..b4d10f417 100644 --- a/plugins/Protector/config.py +++ b/plugins/Protector/config.py @@ -30,6 +30,8 @@ import supybot.conf as conf import supybot.ircutils as ircutils import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Protector') def configure(advanced): # This will be called by supybot to configure this module. advanced is @@ -42,17 +44,17 @@ def configure(advanced): Protector = conf.registerPlugin('Protector') conf.registerChannelValue(Protector, 'enable', - registry.Boolean(True, """Determines whether this plugin is enabled in a - given channel.""")) + registry.Boolean(True, _("""Determines whether this plugin is enabled in a + given channel."""))) class ImmuneNicks(conf.ValidNicks): List = ircutils.IrcSet conf.registerChannelValue(Protector, 'immune', - ImmuneNicks([], """Determines what nicks the bot will consider to + ImmuneNicks([], _("""Determines what nicks the bot will consider to be immune from enforcement. These nicks will not even have their actions watched by this plugin. In general, only the ChanServ for this network - will be in this list.""")) + will be in this list."""))) diff --git a/plugins/Protector/messages.pot b/plugins/Protector/messages.pot new file mode 100644 index 000000000..0bb646f0a --- /dev/null +++ b/plugins/Protector/messages.pot @@ -0,0 +1,31 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-17 18:34+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: config.py:47 +msgid "" +"Determines whether this plugin is enabled in a\n" +" given channel." +msgstr "" + +#: config.py:54 +msgid "" +"Determines what nicks the bot will consider to\n" +" be immune from enforcement. These nicks will not even have their actions\n" +" watched by this plugin. In general, only the ChanServ for this network\n" +" will be in this list." +msgstr "" + diff --git a/plugins/Protector/plugin.py b/plugins/Protector/plugin.py index cae90feeb..b90e3c2de 100644 --- a/plugins/Protector/plugin.py +++ b/plugins/Protector/plugin.py @@ -32,6 +32,8 @@ import supybot.ircdb as ircdb import supybot.ircmsgs as ircmsgs import supybot.ircutils as ircutils import supybot.callbacks as callbacks +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Protector') class Protector(callbacks.Plugin): def isImmune(self, irc, msg): diff --git a/plugins/Quote/config.py b/plugins/Quote/config.py index 39813e5d6..1d1a2cfeb 100644 --- a/plugins/Quote/config.py +++ b/plugins/Quote/config.py @@ -29,6 +29,8 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Quote') def configure(advanced): # This will be called by supybot to configure this module. advanced is @@ -42,7 +44,7 @@ def configure(advanced): Quote = conf.registerPlugin('Quote') # This is where your configuration variables (if any) should go. For example: # conf.registerGlobalValue(Quote, 'someConfigVariableName', -# registry.Boolean(False, """Help for someConfigVariableName.""")) +# registry.Boolean(False, _("""Help for someConfigVariableName."""))) # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/Quote/messages.pot b/plugins/Quote/messages.pot new file mode 100644 index 000000000..7d85fdf6e --- /dev/null +++ b/plugins/Quote/messages.pot @@ -0,0 +1,31 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-17 18:34+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: plugin.py:38 +#, docstring +msgid "" +"[]\n" +"\n" +" Returns a random quote from . is only necessary if\n" +" the message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:47 +msgid "I have no quotes in my database for %s." +msgstr "" + diff --git a/plugins/Quote/plugin.py b/plugins/Quote/plugin.py index c1f0d938a..c18bc750d 100644 --- a/plugins/Quote/plugin.py +++ b/plugins/Quote/plugin.py @@ -29,8 +29,11 @@ from supybot.commands import * import supybot.plugins as plugins +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Quote') class Quote(plugins.ChannelIdDatabasePlugin): + @internationalizeDocstring def random(self, irc, msg, args, channel): """[] @@ -41,7 +44,7 @@ class Quote(plugins.ChannelIdDatabasePlugin): if quote: irc.reply(self.showRecord(quote)) else: - irc.error('I have no quotes in my database for %s.' % channel) + irc.error(_('I have no quotes in my database for %s.') % channel) random = wrap(random, ['channeldb']) Class = Quote diff --git a/plugins/QuoteGrabs/config.py b/plugins/QuoteGrabs/config.py index 27a8f7b0e..18219a8b3 100644 --- a/plugins/QuoteGrabs/config.py +++ b/plugins/QuoteGrabs/config.py @@ -29,6 +29,8 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('QuoteGrabs') def configure(advanced): # This will be called by supybot to configure this module. advanced is @@ -42,24 +44,24 @@ def configure(advanced): QuoteGrabs = conf.registerPlugin('QuoteGrabs') # This is where your configuration variables (if any) should go. For example: # conf.registerGlobalValue(QuoteGrabs, 'someConfigVariableName', -# registry.Boolean(False, """Help for someConfigVariableName.""")) +# registry.Boolean(False, _("""Help for someConfigVariableName."""))) conf.registerChannelValue(conf.supybot.plugins.QuoteGrabs, 'randomGrabber', - registry.Boolean(False, """Determines whether the bot will randomly grab + registry.Boolean(False, _("""Determines whether the bot will randomly grab possibly-suitable quotes on occasion. The suitability of a given message - is determined by ...""")) + is determined by ..."""))) conf.registerChannelValue(conf.supybot.plugins.QuoteGrabs.randomGrabber, 'averageTimeBetweenGrabs', - registry.PositiveInteger(864000, """Determines about how many seconds, on + registry.PositiveInteger(864000, _("""Determines about how many seconds, on average, should elapse between random grabs. This is only an average value; grabs can happen from any time after half this time until never, - although that's unlikely to occur.""")) + although that's unlikely to occur."""))) conf.registerChannelValue(conf.supybot.plugins.QuoteGrabs.randomGrabber, - 'minimumWords', registry.PositiveInteger(3, """Determines the minimum + 'minimumWords', registry.PositiveInteger(3, _("""Determines the minimum number of words in a message for it to be considered for random - grabbing.""")) + grabbing."""))) conf.registerChannelValue(conf.supybot.plugins.QuoteGrabs.randomGrabber, - 'minimumCharacters', registry.PositiveInteger(8, """Determines the + 'minimumCharacters', registry.PositiveInteger(8, _("""Determines the minimum number of characters in a message for it to be considered for - random grabbing.""")) + random grabbing."""))) # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/QuoteGrabs/messages.pot b/plugins/QuoteGrabs/messages.pot new file mode 100644 index 000000000..9b365e4ee --- /dev/null +++ b/plugins/QuoteGrabs/messages.pot @@ -0,0 +1,170 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-17 18:36+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: config.py:49 +msgid "" +"Determines whether the bot will randomly grab\n" +" possibly-suitable quotes on occasion. The suitability of a given message\n" +" is determined by ..." +msgstr "" + +#: config.py:54 +msgid "" +"Determines about how many seconds, on\n" +" average, should elapse between random grabs. This is only an average\n" +" value; grabs can happen from any time after half this time until never,\n" +" although that's unlikely to occur." +msgstr "" + +#: config.py:59 +msgid "" +"Determines the minimum\n" +" number of words in a message for it to be considered for random\n" +" grabbing." +msgstr "" + +#: config.py:63 +msgid "" +"Determines the\n" +" minimum number of characters in a message for it to be considered for\n" +" random grabbing." +msgstr "" + +#: plugin.py:57 +msgid "%s (Said by: %s; grabbed by %s at %t)" +msgstr "" + +#: plugin.py:210 +#, docstring +msgid "Add the help for \"@help QuoteGrabs\" here." +msgstr "" + +#: plugin.py:249 +#, docstring +msgid "" +"[] \n" +"\n" +" Grabs a quote from by for the quotegrabs table.\n" +" is only necessary if the message isn't sent in the channel\n" +" itself.\n" +" " +msgstr "" + +#: plugin.py:262 +msgid "You can't quote grab yourself." +msgstr "" + +#: plugin.py:269 +msgid "I couldn't find a proper message to grab." +msgstr "" + +#: plugin.py:274 +#, docstring +msgid "" +"[] \n" +"\n" +" Removes the grab (the last by default) on .\n" +" is only necessary if the message isn't sent in the channel\n" +" itself.\n" +" " +msgstr "" + +#: plugin.py:285 +msgid "Nothing to ungrab." +msgstr "" + +#: plugin.py:287 +msgid "Invalid grab number." +msgstr "" + +#: plugin.py:292 +#, docstring +msgid "" +"[] \n" +"\n" +" Returns 's latest quote grab in . is only\n" +" necessary if the message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:300 +msgid "I couldn't find a matching quotegrab for %s." +msgstr "" + +#: plugin.py:306 +#, docstring +msgid "" +"[] \n" +"\n" +" Returns a list of shortened quotes that have been grabbed for \n" +" as well as the id of each quote. These ids can be used to get the\n" +" full quote. is only necessary if the message isn't sent in\n" +" the channel itself.\n" +" " +msgstr "" + +#: plugin.py:323 +msgid "I couldn't find any quotegrabs for %s." +msgstr "" + +#: plugin.py:329 +#, docstring +msgid "" +"[] []\n" +"\n" +" Returns a randomly grabbed quote, optionally choosing only from those\n" +" quotes grabbed for . is only necessary if the message\n" +" isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:339 +msgid "Couldn't get a random quote for that nick." +msgstr "" + +#: plugin.py:341 +msgid "Couldn't get a random quote. Are there any grabbed quotes in the database?" +msgstr "" + +#: plugin.py:347 +#, docstring +msgid "" +"[] \n" +"\n" +" Return the quotegrab with the given . is only necessary\n" +" if the message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:355 +msgid "No quotegrab for id %s" +msgstr "" + +#: plugin.py:361 +#, docstring +msgid "" +"[] \n" +"\n" +" Searches for in a quote. is only necessary if the\n" +" message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:376 +msgid "No quotegrabs matching %s" +msgstr "" + diff --git a/plugins/QuoteGrabs/plugin.py b/plugins/QuoteGrabs/plugin.py index 03d3a98d6..9e4f6f77f 100644 --- a/plugins/QuoteGrabs/plugin.py +++ b/plugins/QuoteGrabs/plugin.py @@ -40,6 +40,8 @@ import supybot.ircmsgs as ircmsgs import supybot.plugins as plugins import supybot.ircutils as ircutils import supybot.callbacks as callbacks +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('QuoteGrabs') class QuoteGrabsRecord(dbi.Record): __fields__ = [ @@ -52,7 +54,7 @@ class QuoteGrabsRecord(dbi.Record): def __str__(self): grabber = plugins.getUserName(self.grabber) - return format('%s (Said by: %s; grabbed by %s at %t)', + return format(_('%s (Said by: %s; grabbed by %s at %t)'), self.text, self.hostmask, grabber, self.at) class SqliteQuoteGrabsDB(object): @@ -242,6 +244,7 @@ class QuoteGrabs(callbacks.Plugin): s = 'jots down a new quote for %s' % msg.nick irc.reply(s, action=True, prefixNick=False) + @internationalizeDocstring def grab(self, irc, msg, args, channel, nick): """[] @@ -256,16 +259,17 @@ class QuoteGrabs(callbacks.Plugin): if chan is None: raise callbacks.ArgumentError if ircutils.nickEqual(nick, msg.nick): - irc.error('You can\'t quote grab yourself.', Raise=True) + irc.error(_('You can\'t quote grab yourself.'), Raise=True) for m in reversed(irc.state.history): if m.command == 'PRIVMSG' and ircutils.nickEqual(m.nick, nick) \ and ircutils.strEqual(m.args[0], chan): self._grab(channel, irc, m, msg.prefix) irc.replySuccess() return - irc.error('I couldn\'t find a proper message to grab.') + irc.error(_('I couldn\'t find a proper message to grab.')) grab = wrap(grab, ['channeldb', 'nick']) + @internationalizeDocstring def ungrab(self, irc, msg, args, channel, grab): """[] @@ -278,11 +282,12 @@ class QuoteGrabs(callbacks.Plugin): irc.replySuccess() except dbi.NoRecordError: if grab is None: - irc.error('Nothing to ungrab.') + irc.error(_('Nothing to ungrab.')) else: - irc.error('Invalid grab number.') + irc.error(_('Invalid grab number.')) ungrab = wrap(ungrab, ['channeldb', optional('id')]) + @internationalizeDocstring def quote(self, irc, msg, args, channel, nick): """[] @@ -292,10 +297,11 @@ class QuoteGrabs(callbacks.Plugin): try: irc.reply(self.db.getQuote(channel, nick)) except dbi.NoRecordError: - irc.error('I couldn\'t find a matching quotegrab for %s.' % nick, - Raise=True) + irc.error(_('I couldn\'t find a matching quotegrab for %s.') % + nick, Raise=True) quote = wrap(quote, ['channeldb', 'nick']) + @internationalizeDocstring def list(self, irc, msg, args, channel, nick): """[] @@ -314,10 +320,11 @@ class QuoteGrabs(callbacks.Plugin): L.append(item) irc.reply(utils.str.commaAndify(L)) except dbi.NoRecordError: - irc.error('I couldn\'t find any quotegrabs for %s.' % nick, + irc.error(_('I couldn\'t find any quotegrabs for %s.') % nick, Raise=True) list = wrap(list, ['channeldb', 'nick']) + @internationalizeDocstring def random(self, irc, msg, args, channel, nick): """[] [] @@ -329,12 +336,13 @@ class QuoteGrabs(callbacks.Plugin): irc.reply(self.db.random(channel, nick)) except dbi.NoRecordError: if nick: - irc.error('Couldn\'t get a random quote for that nick.') + irc.error(_('Couldn\'t get a random quote for that nick.')) else: - irc.error('Couldn\'t get a random quote. Are there any ' - 'grabbed quotes in the database?') + irc.error(_('Couldn\'t get a random quote. Are there any ' + 'grabbed quotes in the database?')) random = wrap(random, ['channeldb', additional('nick')]) + @internationalizeDocstring def get(self, irc, msg, args, channel, id): """[] @@ -344,10 +352,11 @@ class QuoteGrabs(callbacks.Plugin): try: irc.reply(self.db.get(channel, id)) except dbi.NoRecordError: - irc.error('No quotegrab for id %s' % utils.str.quoted(id), + irc.error(_('No quotegrab for id %s') % utils.str.quoted(id), Raise=True) get = wrap(get, ['channeldb', 'id']) + @internationalizeDocstring def search(self, irc, msg, args, channel, text): """[] @@ -364,7 +373,7 @@ class QuoteGrabs(callbacks.Plugin): L.append(item) irc.reply(utils.str.commaAndify(L)) except dbi.NoRecordError: - irc.error('No quotegrabs matching %s' % utils.str.quoted(text), + irc.error(_('No quotegrabs matching %s') % utils.str.quoted(text), Raise=True) search = wrap(search, ['channeldb', 'text']) From e358b989747af36d84231660b89af16380ab9e0b Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sun, 17 Oct 2010 18:43:34 +0200 Subject: [PATCH 032/136] Add Debian plugin --- plugins/Debian/BeautifulSoup.py | 1080 +++++++++++++++++++++++++++++++ plugins/Debian/__init__.py | 62 ++ plugins/Debian/config.py | 75 +++ plugins/Debian/plugin.py | 492 ++++++++++++++ plugins/Debian/test.py | 92 +++ 5 files changed, 1801 insertions(+) create mode 100644 plugins/Debian/BeautifulSoup.py create mode 100644 plugins/Debian/__init__.py create mode 100644 plugins/Debian/config.py create mode 100644 plugins/Debian/plugin.py create mode 100644 plugins/Debian/test.py diff --git a/plugins/Debian/BeautifulSoup.py b/plugins/Debian/BeautifulSoup.py new file mode 100644 index 000000000..2541dcc63 --- /dev/null +++ b/plugins/Debian/BeautifulSoup.py @@ -0,0 +1,1080 @@ +"""Beautiful Soup +Elixir and Tonic +"The Screen-Scraper's Friend" +v2.1.1 +http://www.crummy.com/software/BeautifulSoup/ + +Beautiful Soup parses arbitrarily invalid XML- or HTML-like substance +into a tree representation. It provides methods and Pythonic idioms +that make it easy to search and modify the tree. + +A well-formed XML/HTML document will yield a well-formed data +structure. An ill-formed XML/HTML document will yield a +correspondingly ill-formed data structure. If your document is only +locally well-formed, you can use this library to find and process the +well-formed part of it. The BeautifulSoup class has heuristics for +obtaining a sensible parse tree in the face of common HTML errors. + +Beautiful Soup has no external dependencies. It works with Python 2.2 +and up. + +Beautiful Soup defines classes for four different parsing strategies: + + * BeautifulStoneSoup, for parsing XML, SGML, or your domain-specific + language that kind of looks like XML. + + * BeautifulSoup, for parsing run-of-the-mill HTML code, be it valid + or invalid. + + * ICantBelieveItsBeautifulSoup, for parsing valid but bizarre HTML + that trips up BeautifulSoup. + + * BeautifulSOAP, for making it easier to parse XML documents that use + lots of subelements containing a single string, where you'd prefer + they put that string into an attribute (such as SOAP messages). + +You can subclass BeautifulStoneSoup or BeautifulSoup to create a +parsing strategy specific to an XML schema or a particular bizarre +HTML document. Typically your subclass would just override +SELF_CLOSING_TAGS and/or NESTABLE_TAGS. +""" +from __future__ import generators + +__author__ = "Leonard Richardson (leonardr@segfault.org)" +__version__ = "2.1.1" +__date__ = "$Date: 2004/10/18 00:14:20 $" +__copyright__ = "Copyright (c) 2004-2005 Leonard Richardson" +__license__ = "PSF" + +from sgmllib import SGMLParser, SGMLParseError +import types +import re +import sgmllib + +#This code makes Beautiful Soup able to parse XML with namespaces +sgmllib.tagfind = re.compile('[a-zA-Z][-_.:a-zA-Z0-9]*') + +class NullType(object): + + """Similar to NoneType with a corresponding singleton instance + 'Null' that, unlike None, accepts any message and returns itself. + + Examples: + >>> Null("send", "a", "message")("and one more", + ... "and what you get still") is Null + True + """ + + def __new__(cls): return Null + def __call__(self, *args, **kwargs): return Null +## def __getstate__(self, *args): return Null + def __getattr__(self, attr): return Null + def __getitem__(self, item): return Null + def __setattr__(self, attr, value): pass + def __setitem__(self, item, value): pass + def __len__(self): return 0 + # FIXME: is this a python bug? otherwise ``for x in Null: pass`` + # never terminates... + def __iter__(self): return iter([]) + def __contains__(self, item): return False + def __repr__(self): return "Null" +Null = object.__new__(NullType) + +class PageElement: + """Contains the navigational information for some part of the page + (either a tag or a piece of text)""" + + def setup(self, parent=Null, previous=Null): + """Sets up the initial relations between this element and + other elements.""" + self.parent = parent + self.previous = previous + self.next = Null + self.previousSibling = Null + self.nextSibling = Null + if self.parent and self.parent.contents: + self.previousSibling = self.parent.contents[-1] + self.previousSibling.nextSibling = self + + def findNext(self, name=None, attrs={}, text=None): + """Returns the first item that matches the given criteria and + appears after this Tag in the document.""" + return self._first(self.fetchNext, name, attrs, text) + firstNext = findNext + + def fetchNext(self, name=None, attrs={}, text=None, limit=None): + """Returns all items that match the given criteria and appear + before after Tag in the document.""" + return self._fetch(name, attrs, text, limit, self.nextGenerator) + + def findNextSibling(self, name=None, attrs={}, text=None): + """Returns the closest sibling to this Tag that matches the + given criteria and appears after this Tag in the document.""" + return self._first(self.fetchNextSiblings, name, attrs, text) + firstNextSibling = findNextSibling + + def fetchNextSiblings(self, name=None, attrs={}, text=None, limit=None): + """Returns the siblings of this Tag that match the given + criteria and appear after this Tag in the document.""" + return self._fetch(name, attrs, text, limit, self.nextSiblingGenerator) + + def findPrevious(self, name=None, attrs={}, text=None): + """Returns the first item that matches the given criteria and + appears before this Tag in the document.""" + return self._first(self.fetchPrevious, name, attrs, text) + + def fetchPrevious(self, name=None, attrs={}, text=None, limit=None): + """Returns all items that match the given criteria and appear + before this Tag in the document.""" + return self._fetch(name, attrs, text, limit, self.previousGenerator) + firstPrevious = findPrevious + + def findPreviousSibling(self, name=None, attrs={}, text=None): + """Returns the closest sibling to this Tag that matches the + given criteria and appears before this Tag in the document.""" + return self._first(self.fetchPreviousSiblings, name, attrs, text) + firstPreviousSibling = findPreviousSibling + + def fetchPreviousSiblings(self, name=None, attrs={}, text=None, + limit=None): + """Returns the siblings of this Tag that match the given + criteria and appear before this Tag in the document.""" + return self._fetch(name, attrs, text, limit, + self.previousSiblingGenerator) + + def findParent(self, name=None, attrs={}): + """Returns the closest parent of this Tag that matches the given + criteria.""" + r = Null + l = self.fetchParents(name, attrs, 1) + if l: + r = l[0] + return r + firstParent = findParent + + def fetchParents(self, name=None, attrs={}, limit=None): + """Returns the parents of this Tag that match the given + criteria.""" + return self._fetch(name, attrs, None, limit, self.parentGenerator) + + #These methods do the real heavy lifting. + + def _first(self, method, name, attrs, text): + r = Null + l = method(name, attrs, text, 1) + if l: + r = l[0] + return r + + def _fetch(self, name, attrs, text, limit, generator): + "Iterates over a generator looking for things that match." + if not hasattr(attrs, 'items'): + attrs = {'class' : attrs} + + results = [] + g = generator() + while True: + try: + i = g.next() + except StopIteration: + break + found = None + if isinstance(i, Tag): + if not text: + if not name or self._matches(i, name): + match = True + for attr, matchAgainst in attrs.items(): + check = i.get(attr) + if not self._matches(check, matchAgainst): + match = False + break + if match: + found = i + elif text: + if self._matches(i, text): + found = i + if found: + results.append(found) + if limit and len(results) >= limit: + break + return results + + #Generators that can be used to navigate starting from both + #NavigableTexts and Tags. + def nextGenerator(self): + i = self + while i: + i = i.next + yield i + + def nextSiblingGenerator(self): + i = self + while i: + i = i.nextSibling + yield i + + def previousGenerator(self): + i = self + while i: + i = i.previous + yield i + + def previousSiblingGenerator(self): + i = self + while i: + i = i.previousSibling + yield i + + def parentGenerator(self): + i = self + while i: + i = i.parent + yield i + + def _matches(self, chunk, howToMatch): + #print 'looking for %s in %s' % (howToMatch, chunk) + # + # If given a list of items, return true if the list contains a + # text element that matches. + if isList(chunk) and not isinstance(chunk, Tag): + for tag in chunk: + if isinstance(tag, NavigableText) and self._matches(tag, howToMatch): + return True + return False + if callable(howToMatch): + return howToMatch(chunk) + if isinstance(chunk, Tag): + #Custom match methods take the tag as an argument, but all other + #ways of matching match the tag name as a string + chunk = chunk.name + #Now we know that chunk is a string + if not isinstance(chunk, basestring): + chunk = str(chunk) + if hasattr(howToMatch, 'match'): + # It's a regexp object. + return howToMatch.search(chunk) + if isList(howToMatch): + return chunk in howToMatch + if hasattr(howToMatch, 'items'): + return howToMatch.has_key(chunk) + #It's just a string + return str(howToMatch) == chunk + +class NavigableText(PageElement): + + def __getattr__(self, attr): + "For backwards compatibility, text.string gives you text" + if attr == 'string': + return self + else: + raise AttributeError, "'%s' object has no attribute '%s'" % (self.__class__.__name__, attr) + +class NavigableString(str, NavigableText): + pass + +class NavigableUnicodeString(unicode, NavigableText): + pass + +class Tag(PageElement): + + """Represents a found HTML tag with its attributes and contents.""" + + def __init__(self, name, attrs=None, parent=Null, previous=Null): + "Basic constructor." + self.name = name + if attrs == None: + attrs = [] + self.attrs = attrs + self.contents = [] + self.setup(parent, previous) + self.hidden = False + + def get(self, key, default=None): + """Returns the value of the 'key' attribute for the tag, or + the value given for 'default' if it doesn't have that + attribute.""" + return self._getAttrMap().get(key, default) + + def __getitem__(self, key): + """tag[key] returns the value of the 'key' attribute for the tag, + and throws an exception if it's not there.""" + return self._getAttrMap()[key] + + def __iter__(self): + "Iterating over a tag iterates over its contents." + return iter(self.contents) + + def __len__(self): + "The length of a tag is the length of its list of contents." + return len(self.contents) + + def __contains__(self, x): + return x in self.contents + + def __nonzero__(self): + "A tag is non-None even if it has no contents." + return True + + def __setitem__(self, key, value): + """Setting tag[key] sets the value of the 'key' attribute for the + tag.""" + self._getAttrMap() + self.attrMap[key] = value + found = False + for i in range(0, len(self.attrs)): + if self.attrs[i][0] == key: + self.attrs[i] = (key, value) + found = True + if not found: + self.attrs.append((key, value)) + self._getAttrMap()[key] = value + + def __delitem__(self, key): + "Deleting tag[key] deletes all 'key' attributes for the tag." + for item in self.attrs: + if item[0] == key: + self.attrs.remove(item) + #We don't break because bad HTML can define the same + #attribute multiple times. + self._getAttrMap() + if self.attrMap.has_key(key): + del self.attrMap[key] + + def __call__(self, *args, **kwargs): + """Calling a tag like a function is the same as calling its + fetch() method. Eg. tag('a') returns a list of all the A tags + found within this tag.""" + return apply(self.fetch, args, kwargs) + + def __getattr__(self, tag): + if len(tag) > 3 and tag.rfind('Tag') == len(tag)-3: + return self.first(tag[:-3]) + elif tag.find('__') != 0: + return self.first(tag) + + def __eq__(self, other): + """Returns true iff this tag has the same name, the same attributes, + and the same contents (recursively) as the given tag. + + NOTE: right now this will return false if two tags have the + same attributes in a different order. Should this be fixed?""" + if not hasattr(other, 'name') or not hasattr(other, 'attrs') or not hasattr(other, 'contents') or self.name != other.name or self.attrs != other.attrs or len(self) != len(other): + return False + for i in range(0, len(self.contents)): + if self.contents[i] != other.contents[i]: + return False + return True + + def __ne__(self, other): + """Returns true iff this tag is not identical to the other tag, + as defined in __eq__.""" + return not self == other + + def __repr__(self): + """Renders this tag as a string.""" + return str(self) + + def __unicode__(self): + return self.__str__(1) + + def __str__(self, needUnicode=None, showStructureIndent=None): + """Returns a string or Unicode representation of this tag and + its contents. + + NOTE: since Python's HTML parser consumes whitespace, this + method is not certain to reproduce the whitespace present in + the original string.""" + + attrs = [] + if self.attrs: + for key, val in self.attrs: + attrs.append('%s="%s"' % (key, val)) + close = '' + closeTag = '' + if self.isSelfClosing(): + close = ' /' + else: + closeTag = '' % self.name + indentIncrement = None + if showStructureIndent != None: + indentIncrement = showStructureIndent + if not self.hidden: + indentIncrement += 1 + contents = self.renderContents(indentIncrement, needUnicode=needUnicode) + if showStructureIndent: + space = '\n%s' % (' ' * showStructureIndent) + if self.hidden: + s = contents + else: + s = [] + attributeString = '' + if attrs: + attributeString = ' ' + ' '.join(attrs) + if showStructureIndent: + s.append(space) + s.append('<%s%s%s>' % (self.name, attributeString, close)) + s.append(contents) + if closeTag and showStructureIndent != None: + s.append(space) + s.append(closeTag) + s = ''.join(s) + isUnicode = type(s) == types.UnicodeType + if needUnicode and not isUnicode: + s = unicode(s) + elif isUnicode and needUnicode==False: + s = str(s) + return s + + def prettify(self, needUnicode=None): + return self.__str__(needUnicode, showStructureIndent=True) + + def renderContents(self, showStructureIndent=None, needUnicode=None): + """Renders the contents of this tag as a (possibly Unicode) + string.""" + s=[] + for c in self: + text = None + if isinstance(c, NavigableUnicodeString) or type(c) == types.UnicodeType: + text = unicode(c) + elif isinstance(c, Tag): + s.append(c.__str__(needUnicode, showStructureIndent)) + elif needUnicode: + text = unicode(c) + else: + text = str(c) + if text: + if showStructureIndent != None: + if text[-1] == '\n': + text = text[:-1] + s.append(text) + return ''.join(s) + + #Soup methods + + def firstText(self, text, recursive=True): + """Convenience method to retrieve the first piece of text matching the + given criteria. 'text' can be a string, a regular expression object, + a callable that takes a string and returns whether or not the + string 'matches', etc.""" + return self.first(recursive=recursive, text=text) + + def fetchText(self, text, recursive=True, limit=None): + """Convenience method to retrieve all pieces of text matching the + given criteria. 'text' can be a string, a regular expression object, + a callable that takes a string and returns whether or not the + string 'matches', etc.""" + return self.fetch(recursive=recursive, text=text, limit=limit) + + def first(self, name=None, attrs={}, recursive=True, text=None): + """Return only the first child of this + Tag matching the given criteria.""" + r = Null + l = self.fetch(name, attrs, recursive, text, 1) + if l: + r = l[0] + return r + findChild = first + + def fetch(self, name=None, attrs={}, recursive=True, text=None, + limit=None): + """Extracts a list of Tag objects that match the given + criteria. You can specify the name of the Tag and any + attributes you want the Tag to have. + + The value of a key-value pair in the 'attrs' map can be a + string, a list of strings, a regular expression object, or a + callable that takes a string and returns whether or not the + string matches for some custom definition of 'matches'. The + same is true of the tag name.""" + generator = self.recursiveChildGenerator + if not recursive: + generator = self.childGenerator + return self._fetch(name, attrs, text, limit, generator) + fetchChildren = fetch + + #Utility methods + + def isSelfClosing(self): + """Returns true iff this is a self-closing tag as defined in the HTML + standard. + + TODO: This is specific to BeautifulSoup and its subclasses, but it's + used by __str__""" + return self.name in BeautifulSoup.SELF_CLOSING_TAGS + + def append(self, tag): + """Appends the given tag to the contents of this tag.""" + self.contents.append(tag) + + #Private methods + + def _getAttrMap(self): + """Initializes a map representation of this tag's attributes, + if not already initialized.""" + if not getattr(self, 'attrMap'): + self.attrMap = {} + for (key, value) in self.attrs: + self.attrMap[key] = value + return self.attrMap + + #Generator methods + def childGenerator(self): + for i in range(0, len(self.contents)): + yield self.contents[i] + raise StopIteration + + def recursiveChildGenerator(self): + stack = [(self, 0)] + while stack: + tag, start = stack.pop() + if isinstance(tag, Tag): + for i in range(start, len(tag.contents)): + a = tag.contents[i] + yield a + if isinstance(a, Tag) and tag.contents: + if i < len(tag.contents) - 1: + stack.append((tag, i+1)) + stack.append((a, 0)) + break + raise StopIteration + + +def isList(l): + """Convenience method that works with all 2.x versions of Python + to determine whether or not something is listlike.""" + return hasattr(l, '__iter__') \ + or (type(l) in (types.ListType, types.TupleType)) + +def buildTagMap(default, *args): + """Turns a list of maps, lists, or scalars into a single map. + Used to build the SELF_CLOSING_TAGS and NESTABLE_TAGS maps out + of lists and partial maps.""" + built = {} + for portion in args: + if hasattr(portion, 'items'): + #It's a map. Merge it. + for k,v in portion.items(): + built[k] = v + elif isList(portion): + #It's a list. Map each item to the default. + for k in portion: + built[k] = default + else: + #It's a scalar. Map it to the default. + built[portion] = default + return built + +class BeautifulStoneSoup(Tag, SGMLParser): + + """This class contains the basic parser and fetch code. It defines + a parser that knows nothing about tag behavior except for the + following: + + You can't close a tag without closing all the tags it encloses. + That is, "" actually means + "". + + [Another possible explanation is "", but since + this class defines no SELF_CLOSING_TAGS, it will never use that + explanation.] + + This class is useful for parsing XML or made-up markup languages, + or when BeautifulSoup makes an assumption counter to what you were + expecting.""" + + SELF_CLOSING_TAGS = {} + NESTABLE_TAGS = {} + RESET_NESTING_TAGS = {} + QUOTE_TAGS = {} + + #As a public service we will by default silently replace MS smart quotes + #and similar characters with their HTML or ASCII equivalents. + MS_CHARS = { '\x80' : '€', + '\x81' : ' ', + '\x82' : '‚', + '\x83' : 'ƒ', + '\x84' : '„', + '\x85' : '…', + '\x86' : '†', + '\x87' : '‡', + '\x88' : '⁁', + '\x89' : '%', + '\x8A' : 'Š', + '\x8B' : '<', + '\x8C' : 'Œ', + '\x8D' : '?', + '\x8E' : 'Z', + '\x8F' : '?', + '\x90' : '?', + '\x91' : '‘', + '\x92' : '’', + '\x93' : '“', + '\x94' : '”', + '\x95' : '•', + '\x96' : '–', + '\x97' : '—', + '\x98' : '˜', + '\x99' : '™', + '\x9a' : 'š', + '\x9b' : '>', + '\x9c' : 'œ', + '\x9d' : '?', + '\x9e' : 'z', + '\x9f' : 'Ÿ',} + + PARSER_MASSAGE = [(re.compile('(<[^<>]*)/>'), + lambda(x):x.group(1) + ' />'), + (re.compile(']*)>'), + lambda(x):''), + (re.compile("([\x80-\x9f])"), + lambda(x): BeautifulStoneSoup.MS_CHARS.get(x.group(1))) + ] + + ROOT_TAG_NAME = '[document]' + + def __init__(self, text=None, avoidParserProblems=True, + initialTextIsEverything=True): + """Initialize this as the 'root tag' and feed in any text to + the parser. + + NOTE about avoidParserProblems: sgmllib will process most bad + HTML, and BeautifulSoup has tricks for dealing with some HTML + that kills sgmllib, but Beautiful Soup can nonetheless choke + or lose data if your data uses self-closing tags or + declarations incorrectly. By default, Beautiful Soup sanitizes + its input to avoid the vast majority of these problems. The + problems are relatively rare, even in bad HTML, so feel free + to pass in False to avoidParserProblems if they don't apply to + you, and you'll get better performance. The only reason I have + this turned on by default is so I don't get so many tech + support questions. + + The two most common instances of invalid HTML that will choke + sgmllib are fixed by the default parser massage techniques: + +
(No space between name of closing tag and tag close) + (Extraneous whitespace in declaration) + + You can pass in a custom list of (RE object, replace method) + tuples to get Beautiful Soup to scrub your input the way you + want.""" + Tag.__init__(self, self.ROOT_TAG_NAME) + if avoidParserProblems \ + and not isList(avoidParserProblems): + avoidParserProblems = self.PARSER_MASSAGE + self.avoidParserProblems = avoidParserProblems + SGMLParser.__init__(self) + self.quoteStack = [] + self.hidden = 1 + self.reset() + if hasattr(text, 'read'): + #It's a file-type object. + text = text.read() + if text: + self.feed(text) + if initialTextIsEverything: + self.done() + + def __getattr__(self, methodName): + """This method routes method call requests to either the SGMLParser + superclass or the Tag superclass, depending on the method name.""" + if methodName.find('start_') == 0 or methodName.find('end_') == 0 \ + or methodName.find('do_') == 0: + return SGMLParser.__getattr__(self, methodName) + elif methodName.find('__') != 0: + return Tag.__getattr__(self, methodName) + else: + raise AttributeError + + def feed(self, text): + if self.avoidParserProblems: + for fix, m in self.avoidParserProblems: + text = fix.sub(m, text) + SGMLParser.feed(self, text) + + def done(self): + """Called when you're done parsing, so that the unclosed tags can be + correctly processed.""" + self.endData() #NEW + while self.currentTag.name != self.ROOT_TAG_NAME: + self.popTag() + + def reset(self): + SGMLParser.reset(self) + self.currentData = [] + self.currentTag = None + self.tagStack = [] + self.pushTag(self) + + def popTag(self): + tag = self.tagStack.pop() + # Tags with just one string-owning child get the child as a + # 'string' property, so that soup.tag.string is shorthand for + # soup.tag.contents[0] + if len(self.currentTag.contents) == 1 and \ + isinstance(self.currentTag.contents[0], NavigableText): + self.currentTag.string = self.currentTag.contents[0] + + #print "Pop", tag.name + if self.tagStack: + self.currentTag = self.tagStack[-1] + return self.currentTag + + def pushTag(self, tag): + #print "Push", tag.name + if self.currentTag: + self.currentTag.append(tag) + self.tagStack.append(tag) + self.currentTag = self.tagStack[-1] + + def endData(self): + currentData = ''.join(self.currentData) + if currentData: + if not currentData.strip(): + if '\n' in currentData: + currentData = '\n' + else: + currentData = ' ' + c = NavigableString + if type(currentData) == types.UnicodeType: + c = NavigableUnicodeString + o = c(currentData) + o.setup(self.currentTag, self.previous) + if self.previous: + self.previous.next = o + self.previous = o + self.currentTag.contents.append(o) + self.currentData = [] + + def _popToTag(self, name, inclusivePop=True): + """Pops the tag stack up to and including the most recent + instance of the given tag. If inclusivePop is false, pops the tag + stack up to but *not* including the most recent instqance of + the given tag.""" + if name == self.ROOT_TAG_NAME: + return + + numPops = 0 + mostRecentTag = None + for i in range(len(self.tagStack)-1, 0, -1): + if name == self.tagStack[i].name: + numPops = len(self.tagStack)-i + break + if not inclusivePop: + numPops = numPops - 1 + + for i in range(0, numPops): + mostRecentTag = self.popTag() + return mostRecentTag + + def _smartPop(self, name): + + """We need to pop up to the previous tag of this type, unless + one of this tag's nesting reset triggers comes between this + tag and the previous tag of this type, OR unless this tag is a + generic nesting trigger and another generic nesting trigger + comes between this tag and the previous tag of this type. + + Examples: +

FooBar

should pop to 'p', not 'b'. +

FooBar

should pop to 'table', not 'p'. +

Foo

Bar

should pop to 'tr', not 'p'. +

FooBar

should pop to 'p', not 'b'. + +

    • *
    • * should pop to 'ul', not the first 'li'. +
  • ** should pop to 'table', not the first 'tr' + tag should + implicitly close the previous tag within the same
    ** should pop to 'tr', not the first 'td' + """ + + nestingResetTriggers = self.NESTABLE_TAGS.get(name) + isNestable = nestingResetTriggers != None + isResetNesting = self.RESET_NESTING_TAGS.has_key(name) + popTo = None + inclusive = True + for i in range(len(self.tagStack)-1, 0, -1): + p = self.tagStack[i] + if (not p or p.name == name) and not isNestable: + #Non-nestable tags get popped to the top or to their + #last occurance. + popTo = name + break + if (nestingResetTriggers != None + and p.name in nestingResetTriggers) \ + or (nestingResetTriggers == None and isResetNesting + and self.RESET_NESTING_TAGS.has_key(p.name)): + + #If we encounter one of the nesting reset triggers + #peculiar to this tag, or we encounter another tag + #that causes nesting to reset, pop up to but not + #including that tag. + + popTo = p.name + inclusive = False + break + p = p.parent + if popTo: + self._popToTag(popTo, inclusive) + + def unknown_starttag(self, name, attrs, selfClosing=0): + #print "Start tag %s" % name + if self.quoteStack: + #This is not a real tag. + #print "<%s> is not real!" % name + attrs = ''.join(map(lambda(x, y): ' %s="%s"' % (x, y), attrs)) + self.handle_data('<%s%s>' % (name, attrs)) + return + self.endData() + if not name in self.SELF_CLOSING_TAGS and not selfClosing: + self._smartPop(name) + tag = Tag(name, attrs, self.currentTag, self.previous) + if self.previous: + self.previous.next = tag + self.previous = tag + self.pushTag(tag) + if selfClosing or name in self.SELF_CLOSING_TAGS: + self.popTag() + if name in self.QUOTE_TAGS: + #print "Beginning quote (%s)" % name + self.quoteStack.append(name) + self.literal = 1 + + def unknown_endtag(self, name): + if self.quoteStack and self.quoteStack[-1] != name: + #This is not a real end tag. + #print " is not real!" % name + self.handle_data('' % name) + return + self.endData() + self._popToTag(name) + if self.quoteStack and self.quoteStack[-1] == name: + self.quoteStack.pop() + self.literal = (len(self.quoteStack) > 0) + + def handle_data(self, data): + self.currentData.append(data) + + def handle_pi(self, text): + "Propagate processing instructions right through." + self.handle_data("" % text) + + def handle_comment(self, text): + "Propagate comments right through." + self.handle_data("" % text) + + def handle_charref(self, ref): + "Propagate char refs right through." + self.handle_data('&#%s;' % ref) + + def handle_entityref(self, ref): + "Propagate entity refs right through." + self.handle_data('&%s;' % ref) + + def handle_decl(self, data): + "Propagate DOCTYPEs and the like right through." + self.handle_data('' % data) + + def parse_declaration(self, i): + """Treat a bogus SGML declaration as raw data. Treat a CDATA + declaration as regular data.""" + j = None + if self.rawdata[i:i+9] == '', i) + if k == -1: + k = len(self.rawdata) + self.handle_data(self.rawdata[i+9:k]) + j = k+3 + else: + try: + j = SGMLParser.parse_declaration(self, i) + except SGMLParseError: + toHandle = self.rawdata[i:] + self.handle_data(toHandle) + j = i + len(toHandle) + return j + +class BeautifulSoup(BeautifulStoneSoup): + + """This parser knows the following facts about HTML: + + * Some tags have no closing tag and should be interpreted as being + closed as soon as they are encountered. + + * The text inside some tags (ie. 'script') may contain tags which + are not really part of the document and which should be parsed + as text, not tags. If you want to parse the text as tags, you can + always fetch it and parse it explicitly. + + * Tag nesting rules: + + Most tags can't be nested at all. For instance, the occurance of + a

    tag should implicitly close the previous

    tag. + +

    Para1

    Para2 + should be transformed into: +

    Para1

    Para2 + + Some tags can be nested arbitrarily. For instance, the occurance + of a

    tag should _not_ implicitly close the previous +
    tag. + + Alice said:
    Bob said:
    Blah + should NOT be transformed into: + Alice said:
    Bob said:
    Blah + + Some tags can be nested, but the nesting is reset by the + interposition of other tags. For instance, a
    , + but not close a tag in another table. + +
    BlahBlah + should be transformed into: +
    BlahBlah + but, + Blah
    Blah + should NOT be transformed into + Blah
    Blah + + Differing assumptions about tag nesting rules are a major source + of problems with the BeautifulSoup class. If BeautifulSoup is not + treating as nestable a tag your page author treats as nestable, + try ICantBelieveItsBeautifulSoup before writing your own + subclass.""" + + SELF_CLOSING_TAGS = buildTagMap(None, ['br' , 'hr', 'input', 'img', 'meta', + 'spacer', 'link', 'frame', 'base']) + + QUOTE_TAGS = {'script': None} + + #According to the HTML standard, each of these inline tags can + #contain another tag of the same type. Furthermore, it's common + #to actually use these tags this way. + NESTABLE_INLINE_TAGS = ['span', 'font', 'q', 'object', 'bdo', 'sub', 'sup', + 'center'] + + #According to the HTML standard, these block tags can contain + #another tag of the same type. Furthermore, it's common + #to actually use these tags this way. + NESTABLE_BLOCK_TAGS = ['blockquote', 'div', 'fieldset', 'ins', 'del'] + + #Lists can contain other lists, but there are restrictions. + NESTABLE_LIST_TAGS = { 'ol' : [], + 'ul' : [], + 'li' : ['ul', 'ol'], + 'dl' : [], + 'dd' : ['dl'], + 'dt' : ['dl'] } + + #Tables can contain other tables, but there are restrictions. + NESTABLE_TABLE_TAGS = {'table' : [], + 'tr' : ['table', 'tbody', 'tfoot', 'thead'], + 'td' : ['tr'], + 'th' : ['tr'], + } + + NON_NESTABLE_BLOCK_TAGS = ['address', 'form', 'p', 'pre'] + + #If one of these tags is encountered, all tags up to the next tag of + #this type are popped. + RESET_NESTING_TAGS = buildTagMap(None, NESTABLE_BLOCK_TAGS, 'noscript', + NON_NESTABLE_BLOCK_TAGS, + NESTABLE_LIST_TAGS, + NESTABLE_TABLE_TAGS) + + NESTABLE_TAGS = buildTagMap([], NESTABLE_INLINE_TAGS, NESTABLE_BLOCK_TAGS, + NESTABLE_LIST_TAGS, NESTABLE_TABLE_TAGS) + +class ICantBelieveItsBeautifulSoup(BeautifulSoup): + + """The BeautifulSoup class is oriented towards skipping over + common HTML errors like unclosed tags. However, sometimes it makes + errors of its own. For instance, consider this fragment: + + FooBar + + This is perfectly valid (if bizarre) HTML. However, the + BeautifulSoup class will implicitly close the first b tag when it + encounters the second 'b'. It will think the author wrote + "FooBar", and didn't close the first 'b' tag, because + there's no real-world reason to bold something that's already + bold. When it encounters '' it will close two more 'b' + tags, for a grand total of three tags closed instead of two. This + can throw off the rest of your document structure. The same is + true of a number of other tags, listed below. + + It's much more common for someone to forget to close (eg.) a 'b' + tag than to actually use nested 'b' tags, and the BeautifulSoup + class handles the common case. This class handles the + not-co-common case: where you can't believe someone wrote what + they did, but it's valid HTML and BeautifulSoup screwed up by + assuming it wouldn't be. + + If this doesn't do what you need, try subclassing this class or + BeautifulSoup, and providing your own list of NESTABLE_TAGS.""" + + I_CANT_BELIEVE_THEYRE_NESTABLE_INLINE_TAGS = \ + ['em', 'big', 'i', 'small', 'tt', 'abbr', 'acronym', 'strong', + 'cite', 'code', 'dfn', 'kbd', 'samp', 'strong', 'var', 'b', + 'big'] + + I_CANT_BELIEVE_THEYRE_NESTABLE_BLOCK_TAGS = ['noscript'] + + NESTABLE_TAGS = buildTagMap([], BeautifulSoup.NESTABLE_TAGS, + I_CANT_BELIEVE_THEYRE_NESTABLE_BLOCK_TAGS, + I_CANT_BELIEVE_THEYRE_NESTABLE_INLINE_TAGS) + +class BeautifulSOAP(BeautifulStoneSoup): + """This class will push a tag with only a single string child into + the tag's parent as an attribute. The attribute's name is the tag + name, and the value is the string child. An example should give + the flavor of the change: + + baz + => + baz + + You can then access fooTag['bar'] instead of fooTag.barTag.string. + + This is, of course, useful for scraping structures that tend to + use subelements instead of attributes, such as SOAP messages. Note + that it modifies its input, so don't print the modified version + out. + + I'm not sure how many people really want to use this class; let me + know if you do. Mainly I like the name.""" + + def popTag(self): + if len(self.tagStack) > 1: + tag = self.tagStack[-1] + parent = self.tagStack[-2] + parent._getAttrMap() + if (isinstance(tag, Tag) and len(tag.contents) == 1 and + isinstance(tag.contents[0], NavigableText) and + not parent.attrMap.has_key(tag.name)): + parent[tag.name] = tag.contents[0] + BeautifulStoneSoup.popTag(self) + +#Enterprise class names! It has come to our attention that some people +#think the names of the Beautiful Soup parser classes are too silly +#and "unprofessional" for use in enterprise screen-scraping. We feel +#your pain! For such-minded folk, the Beautiful Soup Consortium And +#All-Night Kosher Bakery recommends renaming this file to +#"RobustParser.py" (or, in cases of extreme enterprisitude, +#"RobustParserBeanInterface.class") and using the following +#enterprise-friendly class aliases: +class RobustXMLParser(BeautifulStoneSoup): + pass +class RobustHTMLParser(BeautifulSoup): + pass +class RobustWackAssHTMLParser(ICantBelieveItsBeautifulSoup): + pass +class SimplifyingSOAPParser(BeautifulSOAP): + pass + +### + + +#By default, act as an HTML pretty-printer. +if __name__ == '__main__': + import sys + soup = BeautifulStoneSoup(sys.stdin.read()) + print soup.prettify() diff --git a/plugins/Debian/__init__.py b/plugins/Debian/__init__.py new file mode 100644 index 000000000..535f2ff85 --- /dev/null +++ b/plugins/Debian/__init__.py @@ -0,0 +1,62 @@ +### +# Copyright (c) 2003-2005, James Vega +# 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. +### + +""" +This is a module to contain Debian-specific commands. +""" + +import supybot +import supybot.world as world + +# Use this for the version of this plugin. You may wish to put a CVS keyword +# in here if you're keeping the plugin in CVS or some similar system. +__version__ = "0.1" + +__author__ = supybot.authors.jamessan + +# This is a dictionary mapping supybot.Author instances to lists of +# contributions. +__contributors__ = {} + +import config +import plugin +reload(plugin) # In case we're being reloaded. +# Add more reloads here if you add third-party modules and want them to be +# reloaded when this plugin is reloaded. Don't forget to import them as well! +import BeautifulSoup +reload(BeautifulSoup) + +if world.testing: + import test + +Class = plugin.Class +configure = config.configure + + +# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/Debian/config.py b/plugins/Debian/config.py new file mode 100644 index 000000000..efd8a8c51 --- /dev/null +++ b/plugins/Debian/config.py @@ -0,0 +1,75 @@ +### +# Copyright (c) 2003-2005, James Vega +# 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. +### + +import supybot.conf as conf +import supybot.registry as registry + +def configure(advanced): + # This will be called by supybot to configure this module. advanced is + # a bool that specifies whether the user identified himself as an advanced + # user or not. You should effect your configuration by manipulating the + # registry as appropriate. + from supybot.questions import output, expect, anything, something, yn + conf.registerPlugin('Debian', True) + if not utils.findBinaryInPath('zgrep'): + if not advanced: + output("""I can't find zgrep in your path. This is necessary + to run the file command. I'll disable this command + now. When you get zgrep in your path, use the command + 'enable Debian.file' to re-enable the command.""") + capabilities = conf.supybot.capabilities() + capabilities.add('-Debian.file') + conf.supybot.capabilities.set(capabilities) + else: + output("""I can't find zgrep in your path. If you want to run + the file command with any sort of expediency, you'll + need it. You can use a python equivalent, but it's + about two orders of magnitude slower. THIS MEANS IT + WILL TAKE AGES TO RUN THIS COMMAND. Don't do this.""") + if yn('Do you want to use a Python equivalent of zgrep?'): + conf.supybot.plugins.Debian.pythonZgrep.setValue(True) + else: + output('I\'ll disable file now.') + capabilities = conf.supybot.capabilities() + capabilities.add('-Debian.file') + conf.supybot.capabilities.set(capabilities) + + +Debian = conf.registerPlugin('Debian') +conf.registerGlobalValue(Debian, 'pythonZgrep', + registry.Boolean(False, """An advanced option, mostly just for testing; + uses a Python-coded zgrep rather than the actual zgrep executable, + generally resulting in a 50x slowdown. What would take 2 seconds will + take 100 with this enabled. Don't enable this.""")) +conf.registerChannelValue(Debian, 'bold', + registry.Boolean(True, """Determines whether the plugin will use bold in + the responses to some of its commands.""")) + + +# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/Debian/plugin.py b/plugins/Debian/plugin.py new file mode 100644 index 000000000..2bc089ab3 --- /dev/null +++ b/plugins/Debian/plugin.py @@ -0,0 +1,492 @@ +### +# Copyright (c) 2003-2005, James Vega +# 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. +### + +import os +import re +import gzip +import time +import popen2 +import fnmatch +import threading + +import BeautifulSoup + +import supybot.conf as conf +import supybot.utils as utils +import supybot.world as world +from supybot.commands import * +import supybot.plugins as plugins +import supybot.ircutils as ircutils +import supybot.callbacks as callbacks +from supybot.utils.iter import all, imap, ifilter + +class PeriodicFileDownloader(object): + """A class to periodically download a file/files. + + A class-level dictionary 'periodicFiles' maps names of files to + three-tuples of + (url, seconds between downloads, function to run with downloaded file). + + 'url' should be in some form that urllib2.urlopen can handle (do note that + urllib2.urlopen handles file:// links perfectly well.) + + 'seconds between downloads' is the number of seconds between downloads, + obviously. An important point to remember, however, is that it is only + engaged when a command is run. I.e., if you say you want the file + downloaded every day, but no commands that use it are run in a week, the + next time such a command is run, it'll be using a week-old file. If you + don't want such behavior, you'll have to give an error mess age to the user + and tell him to call you back in the morning. + + 'function to run with downloaded file' is a function that will be passed + a string *filename* of the downloaded file. This will be some random + filename probably generated via some mktemp-type-thing. You can do what + you want with this; you may want to build a database, take some stats, + or simply rename the file. You can pass None as your function and the + file with automatically be renamed to match the filename you have it listed + under. It'll be in conf.supybot.directories.data, of course. + + Aside from that dictionary, simply use self.getFile(filename) in any method + that makes use of a periodically downloaded file, and you'll be set. + """ + periodicFiles = None + def __init__(self, *args, **kwargs): + if self.periodicFiles is None: + raise ValueError, 'You must provide files to download' + self.lastDownloaded = {} + self.downloadedCounter = {} + for filename in self.periodicFiles: + if self.periodicFiles[filename][-1] is None: + fullname = os.path.join(conf.supybot.directories.data(), + filename) + if os.path.exists(fullname): + self.lastDownloaded[filename] = os.stat(fullname).st_ctime + else: + self.lastDownloaded[filename] = 0 + else: + self.lastDownloaded[filename] = 0 + self.currentlyDownloading = set() + self.downloadedCounter[filename] = 0 + self.getFile(filename) + super(PeriodicFileDownloader, self).__init__(*args, **kwargs) + + def _downloadFile(self, filename, url, f): + self.currentlyDownloading.add(filename) + try: + try: + infd = utils.web.getUrlFd(url) + except IOError, e: + self.log.warning('Error downloading %s: %s', url, e) + return + except utils.web.Error, e: + self.log.warning('Error downloading %s: %s', url, e) + return + confDir = conf.supybot.directories.data() + newFilename = os.path.join(confDir, utils.file.mktemp()) + outfd = file(newFilename, 'wb') + start = time.time() + s = infd.read(4096) + while s: + outfd.write(s) + s = infd.read(4096) + infd.close() + outfd.close() + self.log.info('Downloaded %s in %s seconds', + filename, time.time()-start) + self.downloadedCounter[filename] += 1 + self.lastDownloaded[filename] = time.time() + if f is None: + toFilename = os.path.join(confDir, filename) + if os.name == 'nt': + # Windows, grrr... + if os.path.exists(toFilename): + os.remove(toFilename) + os.rename(newFilename, toFilename) + else: + start = time.time() + f(newFilename) + total = time.time() - start + self.log.info('Function ran on %s in %s seconds', + filename, total) + finally: + self.currentlyDownloading.remove(filename) + + def getFile(self, filename): + if world.documenting: + return + (url, timeLimit, f) = self.periodicFiles[filename] + if time.time() - self.lastDownloaded[filename] > timeLimit and \ + filename not in self.currentlyDownloading: + self.log.info('Beginning download of %s', url) + args = (filename, url, f) + name = '%s #%s' % (filename, self.downloadedCounter[filename]) + t = threading.Thread(target=self._downloadFile, name=name, + args=(filename, url, f)) + t.setDaemon(True) + t.start() + world.threadsSpawned += 1 + + +class Debian(callbacks.Plugin, PeriodicFileDownloader): + threaded = True + periodicFiles = { + # This file is only updated once a week, so there's no sense in + # downloading a new one every day. + 'Contents-i386.gz': ('ftp://ftp.us.debian.org/' + 'debian/dists/unstable/Contents-i386.gz', + 604800, None) + } + contents = conf.supybot.directories.data.dirize('Contents-i386.gz') + def file(self, irc, msg, args, optlist, glob): + """[--{regexp,exact} ] [] + + Returns packages in Debian that includes files matching . If + --regexp is given, returns packages that include files matching the + given regexp. If --exact is given, returns packages that include files + matching exactly the string given. + """ + self.getFile('Contents-i386.gz') + # Make sure it's anchored, make sure it doesn't have a leading slash + # (the filenames don't have leading slashes, and people may not know + # that). + if not optlist and not glob: + raise callbacks.ArgumentError + if optlist and glob: + irc.error('You must specify either a glob or a regexp/exact ' + 'search, but not both.', Raise=True) + for (option, arg) in optlist: + if option == 'exact': + regexp = arg.lstrip('/') + elif option == 'regexp': + regexp = arg + if glob: + regexp = fnmatch.translate(glob.lstrip('/')) + regexp = regexp.rstrip('$') + regexp = ".*%s.* " % regexp + try: + re_obj = re.compile(regexp, re.I) + except re.error, e: + irc.error(format('Error in regexp: %s', e), Raise=True) + if self.registryValue('pythonZgrep'): + fd = gzip.open(self.contents) + r = imap(lambda tup: tup[0], + ifilter(lambda tup: tup[0], + imap(lambda line:(re_obj.search(line), line),fd))) + else: + try: + (r, w) = popen2.popen4(['zgrep', '-ie', regexp, self.contents]) + w.close() + except TypeError: + # We're on Windows. + irc.error('This command won\'t work on this platform. ' + 'If you think it should (i.e., you know that you ' + 'have a zgrep binary somewhere) then file a bug ' + 'about it at http://supybot.sf.net/ .', Raise=True) + packages = set() # Make packages unique + try: + for line in r: + if len(packages) > 100: + irc.error('More than 100 packages matched, ' + 'please narrow your search.', Raise=True) + try: + if hasattr(line, 'group'): # we're actually using + line = line.group(0) # pythonZgrep :( + (filename, pkg_list) = line.split() + if filename == 'FILE': + # This is the last line before the actual files. + continue + except ValueError: # Unpack list of wrong size. + continue # We've not gotten to the files yet. + packages.update(pkg_list.split(',')) + finally: + if hasattr(r, 'close'): + r.close() + if len(packages) == 0: + irc.reply('I found no packages with that file.') + else: + irc.reply(format('%L', sorted(packages))) + file = wrap(file, [getopts({'regexp':'regexpMatcher','exact':'something'}), + additional('glob')]) + + _debreflags = re.DOTALL | re.IGNORECASE + _deblistre = re.compile(r'

    Package ([^<]+)

    (.*?)', _debreflags) + def version(self, irc, msg, args, optlist, branch, package): + """[--exact] [{stable,testing,unstable,experimental}] + + Returns the current version(s) of a Debian package in the given branch + (if any, otherwise all available ones are displayed). If --exact is + specified, only packages whose name exactly matches + will be reported. + """ + url = 'http://packages.debian.org/cgi-bin/search_packages.pl?keywords'\ + '=%s&searchon=names&version=%s&release=all&subword=1' + for (option, _) in optlist: + if option == 'exact': + url = url.replace('&subword=1','') + responses = [] + if '*' in package: + irc.error('Wildcard characters can not be specified.', Raise=True) + package = utils.web.urlquote(package) + url %= (package, branch) + try: + html = utils.web.getUrl(url) + except utils.web.Error, e: + irc.error(format('I couldn\'t reach the search page (%s).', e), + Raise=True) + if 'is down at the moment' in html: + irc.error('Packages.debian.org is down at the moment. ' + 'Please try again later.', Raise=True) + pkgs = self._deblistre.findall(html) + if not pkgs: + irc.reply(format('No package found for %s (%s)', + utils.web.urlunquote(package), branch)) + else: + for pkg in pkgs: + pkgMatch = pkg[0] + soup = BeautifulSoup.BeautifulSoup() + soup.feed(pkg[1]) + liBranches = soup.fetch('li') + branches = [] + versions = [] + def branchVers(br): + vers = [b.next.string.strip() for b in br] + return [utils.str.rsplit(v, ':', 1)[0] for v in vers] + for li in liBranches: + branches.append(li.a.string) + versions.append(branchVers(li.fetch('br'))) + if branches and versions: + for pairs in zip(branches, versions): + branch = pairs[0] + ver = ', '.join(pairs[1]) + s = format('%s (%s)', pkgMatch, + ': '.join([branch, ver])) + responses.append(s) + resp = format('%i matches found: %s', + len(responses), '; '.join(responses)) + irc.reply(resp) + version = wrap(version, [getopts({'exact':''}), + optional(('literal', ('stable', 'testing', + 'unstable', 'experimental')), 'all'), + 'text']) + + _incomingRe = re.compile(r'', re.I) + def incoming(self, irc, msg, args, optlist, globs): + """[--{regexp,arch} ] [ ...] + + Checks debian incoming for a matching package name. The arch + parameter defaults to i386; --regexp returns only those package names + that match a given regexp, and normal matches use standard *nix + globbing. + """ + predicates = [] + archPredicate = lambda s: ('_i386.' in s) + for (option, arg) in optlist: + if option == 'regexp': + predicates.append(r.search) + elif option == 'arch': + arg = '_%s.' % arg + archPredicate = lambda s, arg=arg: (arg in s) + predicates.append(archPredicate) + for glob in globs: + glob = fnmatch.translate(glob) + predicates.append(re.compile(glob).search) + packages = [] + try: + fd = utils.web.getUrlFd('http://incoming.debian.org/') + except utils.web.Error, e: + irc.error(str(e), Raise=True) + for line in fd: + m = self._incomingRe.search(line) + if m: + name = m.group(1) + if all(None, imap(lambda p: p(name), predicates)): + realname = utils.str.rsplit(name, '_', 1)[0] + packages.append(realname) + if len(packages) == 0: + irc.error('No packages matched that search.') + else: + irc.reply(format('%L', packages)) + incoming = thread(wrap(incoming, + [getopts({'regexp': 'regexpMatcher', + 'arch': 'something'}), + any('glob')])) + + def bold(self, s): + if self.registryValue('bold', dynamic.channel): + return ircutils.bold(s) + return s + + _update = re.compile(r' : ([^<]+) + + Reports various statistics (from http://packages.qa.debian.org/) about + . + """ + pkg = pkg.lower() + text = utils.web.getUrl('http://packages.qa.debian.org/%s/%s.html' % + (pkg[0], pkg)) + if "Error 404" in text: + irc.errorInvalid('source package name') + updated = None + m = self._update.search(text) + if m: + updated = m.group(1) + soup = BeautifulSoup.BeautifulSoup() + soup.feed(text) + pairs = zip(soup.fetch('td', {'class': 'labelcell'}), + soup.fetch('td', {'class': 'contentcell'})) + for (label, content) in pairs: + if label.string == 'Last version': + version = '%s: %s' % (self.bold(label.string), content.string) + elif label.string == 'Maintainer': + name = content.a.string + email = content.fetch('a')[1]['href'][7:] + maintainer = format('%s: %s %u', self.bold('Maintainer'), + name, utils.web.mungeEmail(email)) + elif label.string == 'All bugs': + bugsAll = format('%i Total', content.first('a').string) + elif label.string == 'Release Critical': + bugsRC = format('%i RC', content.first('a').string) + elif label.string == 'Important and Normal': + bugs = format('%i Important/Normal', + content.first('a').string) + elif label.string == 'Minor and Wishlist': + bugsMinor = format('%i Minor/Wishlist', + content.first('a').string) + elif label.string == 'Fixed and Pending': + bugsFixed = format('%i Fixed/Pending', + content.first('a').string) + elif label.string == 'Subscribers count': + subscribers = format('%s: %i', + self.bold('Subscribers'), content.string) + bugL = (bugsAll, bugsRC, bugs, bugsMinor, bugsFixed) + s = '. '.join((version, maintainer, subscribers, + '%s: %s' % (self.bold('Bugs'), '; '.join(bugL)))) + if updated: + s = 'As of %s, %s' % (updated, s) + irc.reply(s) + stats = wrap(stats, ['somethingWithoutSpaces']) + + _newpkgre = re.compile(r'
  • ]+>([^<]+)') + def new(self, irc, msg, args, section, glob): + """[{main,contrib,non-free}] [] + + Checks for packages that have been added to Debian's unstable branch + in the past week. If no glob is specified, returns a list of all + packages. If no section is specified, defaults to main. + """ + try: + fd = utils.web.getUrlFd( + 'http://packages.debian.org/unstable/newpkg_%s' % section) + except utils.web.Error, e: + irc.error(str(e), Raise=True) + packages = [] + for line in fd: + m = self._newpkgre.search(line) + if m: + m = m.group(1) + if fnmatch.fnmatch(m, glob): + packages.append(m) + fd.close() + if packages: + irc.reply(format('%L', packages)) + else: + irc.error('No packages matched that search.') + new = wrap(new, [optional(('literal', ('main', 'contrib', 'non-free')), + 'main'), + additional('glob', '*')]) + + _severity = re.compile(r'.*(?:severity set to `([^\']+)\'|' + r'severity:\s+([^<]+))', re.I) + _package = re.compile(r'Package: <[^>]+>([^<]+)<', re.I | re.S) + _reporter = re.compile(r'Reported by: <[^>]+>([^<]+)<', re.I | re.S) + _subject = re.compile(r'
    ([^<]+)', re.I | re.S) + _date = re.compile(r'Date: ([^;]+);', re.I | re.S) + _tags = re.compile(r'Tags: ([^<]+)', re.I) + _searches = (_package, _subject, _reporter, _date) + def bug(self, irc, msg, args, bug): + """ + + Returns a description of the bug with bug id . + """ + url = 'http://bugs.debian.org/%s' % bug + try: + text = utils.web.getUrl(url) + except utils.web.Error, e: + irc.error(str(e), Raise=True) + if "There is no record of Bug" in text: + irc.error('I could not find a bug report matching that number.', + Raise=True) + searches = map(lambda p: p.search(text), self._searches) + sev = self._severity.search(text) + tags = self._tags.search(text) + # This section should be cleaned up to ease future modifications + if all(None, searches): + L = map(self.bold, ('Package', 'Subject', 'Reported')) + resp = format('%s: %%s; %s: %%s; %s: by %%s on %%s', *L) + L = map(utils.web.htmlToText, map(lambda p: p.group(1), searches)) + resp = format(resp, *L) + if sev: + sev = filter(None, sev.groups()) + if sev: + sev = utils.web.htmlToText(sev[0]) + resp += format('; %s: %s', self.bold('Severity'), sev) + if tags: + resp += format('; %s: %s', self.bold('Tags'), tags.group(1)) + resp += format('; %u', url) + irc.reply(resp) + else: + irc.reply('I was unable to properly parse the BTS page.') + bug = wrap(bug, [('id', 'bug')]) + + _dpnRe = re.compile(r'"\+2">([^<]+) + + Turns into a 'debian package name' using + http://www.pigdog.com/features/dpn.html. + """ + url = r'http://www.pigdog.org/cgi_bin/dpn.phtml?name=%s' + try: + text = utils.web.getUrl(url % '+'.join(words)) + except utils.web.Error, e: + irc.error(str(e), Raise=True) + m = self._dpnRe.search(text) + if m is not None: + irc.reply(m.group(1)) + else: + irc.errorPossibleBug('Unable to parse webpage.') + debianize = wrap(debianize, [many('something')]) + + +Class = Debian + + +# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/Debian/test.py b/plugins/Debian/test.py new file mode 100644 index 000000000..8fc0b5c08 --- /dev/null +++ b/plugins/Debian/test.py @@ -0,0 +1,92 @@ +### +# Copyright (c) 2003-2005, James Vega +# 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. +### + +import os +import time + +from supybot.test import * + +class DebianTestCase(PluginTestCase): + plugins = ('Debian',) + timeout = 100 + cleanDataDir = False + fileDownloaded = False + if network: + def setUp(self, nick='test'): + PluginTestCase.setUp(self) + try: + datadir = conf.supybot.directories.data + if os.path.exists(datadir.dirize('Contents-i386.gz')): + pass + else: + print + print "Downloading files, this may take awhile." + filename = datadir.dirize('Contents-i386.gz') + while not os.path.exists(filename): + time.sleep(1) + print "Download complete." + print "Starting test ..." + self.fileDownloaded = True + except KeyboardInterrupt: + pass + + def testDebBugNoHtml(self): + self.assertNotRegexp('debian bug 287792', r'\') + + def testDebversion(self): + self.assertHelp('debian version') + self.assertRegexp('debian version lakjdfad', + r'^No package.*\(all\)') + self.assertRegexp('debian version unstable alkdjfad', + r'^No package.*\(unstable\)') + self.assertRegexp('debian version gaim', + r'\d+ matches found:.*gaim.*\(stable') + self.assertRegexp('debian version linux-wlan', + r'\d+ matches found:.*linux-wlan.*') + self.assertRegexp('debian version --exact linux-wlan', + r'^No package.*\(all\)') + self.assertError('debian version unstable') + + def testDebfile(self): + self.assertHelp('file') + if not self.fileDownloaded: + pass + self.assertRegexp('file --exact bin/gaim', r'net/gaim') + + def testDebincoming(self): + self.assertNotError('incoming') + + def testDebianize(self): + self.assertNotError('debianize supybot') + + def testDebstats(self): + self.assertNotError('stats supybot') + + +# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: From f613eb0f45a860cfb72c91f92fad0efe763ad7c2 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sun, 17 Oct 2010 19:59:13 +0200 Subject: [PATCH 033/136] Add SupySandbox plugin --- plugins/SupySandbox/README.txt | 1 + plugins/SupySandbox/__init__.py | 68 +++++++ plugins/SupySandbox/config.py | 50 +++++ plugins/SupySandbox/local/__init__.py | 1 + plugins/SupySandbox/local/fschfsch.py | 279 ++++++++++++++++++++++++++ plugins/SupySandbox/plugin.py | 199 ++++++++++++++++++ plugins/SupySandbox/test.py | 56 ++++++ 7 files changed, 654 insertions(+) create mode 100644 plugins/SupySandbox/README.txt create mode 100644 plugins/SupySandbox/__init__.py create mode 100644 plugins/SupySandbox/config.py create mode 100644 plugins/SupySandbox/local/__init__.py create mode 100644 plugins/SupySandbox/local/fschfsch.py create mode 100644 plugins/SupySandbox/plugin.py create mode 100644 plugins/SupySandbox/test.py diff --git a/plugins/SupySandbox/README.txt b/plugins/SupySandbox/README.txt new file mode 100644 index 000000000..d60b47a97 --- /dev/null +++ b/plugins/SupySandbox/README.txt @@ -0,0 +1 @@ +Insert a description of your plugin here, with any notes, etc. about using it. diff --git a/plugins/SupySandbox/__init__.py b/plugins/SupySandbox/__init__.py new file mode 100644 index 000000000..5e5a07162 --- /dev/null +++ b/plugins/SupySandbox/__init__.py @@ -0,0 +1,68 @@ +# -*- coding: 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. + +### + +""" +Ce plugin est un portage du robot IRC Fschfsch, servant à fournir un accès +à la pysandbox sur IRC. +""" + +import supybot +import supybot.world as world + +# Use this for the version of this plugin. You may wish to put a CVS keyword +# in here if you're keeping the plugin in CVS or some similar system. +__version__ = "0.1" + +# XXX Replace this with an appropriate author or supybot.Author instance. +__author__ = supybot.Author('Valentin Lorentz', 'ProgVal', + 'progval@gmail.com') + +# This is a dictionary mapping supybot.Author instances to lists of +# contributions. +__contributors__ = {} + +# This is a url where the most recent plugin package can be downloaded. +__url__ = 'http://supybot-fr.tk/SupySandbox' + +import config +import plugin +reload(plugin) # In case we're being reloaded. +# Add more reloads here if you add third-party modules and want them to be +# reloaded when this plugin is reloaded. Don't forget to import them as well! + +if world.testing: + import test + +Class = plugin.Class +configure = config.configure + + +# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: diff --git a/plugins/SupySandbox/config.py b/plugins/SupySandbox/config.py new file mode 100644 index 000000000..748065cad --- /dev/null +++ b/plugins/SupySandbox/config.py @@ -0,0 +1,50 @@ +# -*- coding: 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. + +### + +import supybot.conf as conf +import supybot.registry as registry + +def configure(advanced): + # This will be called by supybot to configure this module. advanced is + # a bool that specifies whether the user identified himself as an advanced + # user or not. You should effect your configuration by manipulating the + # registry as appropriate. + from supybot.questions import expect, anything, something, yn + conf.registerPlugin('SupySandbox', True) + + +PySandbox = conf.registerPlugin('SupySandbox') +# This is where your configuration variables (if any) should go. For example: +# conf.registerGlobalValue(PySandbox, 'someConfigVariableName', +# registry.Boolean(False, """Help for someConfigVariableName.""")) + + +# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: diff --git a/plugins/SupySandbox/local/__init__.py b/plugins/SupySandbox/local/__init__.py new file mode 100644 index 000000000..e86e97b86 --- /dev/null +++ b/plugins/SupySandbox/local/__init__.py @@ -0,0 +1 @@ +# Stub so local is a module, used for third-party modules diff --git a/plugins/SupySandbox/local/fschfsch.py b/plugins/SupySandbox/local/fschfsch.py new file mode 100644 index 000000000..20872fae6 --- /dev/null +++ b/plugins/SupySandbox/local/fschfsch.py @@ -0,0 +1,279 @@ +#!/usr/bin/env python +# this file is under the WTFPLv2 [http://sam.zoy.org/wtfpl] +# v1: 2010/05/23 +# Author: Tila + +# You need a configuration file: ~/.fschfsch.py. Config example: +# --- +# host = 'irc.freenode.net' +# port = 7000 +# ssl = True +# nickname = 'botnickname' +# password = 'secret' +# channels = ['##fschfsch', '#channel2', '#channel3'] +# texts = {'help': 'I am fschfsch, a robot snake that evals python code', +# 'sandbox': "I am powered by setrlimit and pysandbox [http://github.com/haypo/pysandbox], I don't fear you"} +# --- + +''' +fschfsch is a Python-evaluating bot. fschfsch is pronounced "fssshh! fssshh!". +''' + +IN_MAXLEN = 300 # bytes +OUT_MAXLEN = 300 # bytes +TIMEOUT = 3 # seconds + +EVAL_MAXTIMESECONDS = TIMEOUT +EVAL_MAXMEMORYBYTES = 10 * 1024 * 1024 # 10 MiB + + +try: + import sandbox as S +except ImportError: + print 'You need pysandbox in order to run fschfsch [http://github.com/haypo/pysandbox].' + raise +try: + import twisted +except ImportError: + print 'You need twisted in order to run fschfsch.' + raise +from twisted.internet.protocol import ReconnectingClientFactory +from twisted.internet import ssl, reactor +from twisted.words.im.ircsupport import IRCProto +from twisted.words.protocols.irc import IRCClient +# other imports +import re +import sys +import os +import resource as R +import select +import signal +import time +import threading +import random + +def createSandboxConfig(): + cfg = S.SandboxConfig( + 'stdout', + 'stderr', + 'regex', + 'unicodedata', # flow wants u'\{ATOM SYMBOL}' :-) + 'future', + 'code', + 'time', + 'datetime', + 'math', + 'itertools', + 'random', + 'encodings', + ) + cfg.allowModule('sys', + 'version', 'hexversion', 'version_info') + return cfg + +def _evalPython(line, locals): + locals = dict(locals) + try: + if "\n" in line: + raise SyntaxError() + code = compile(line, "", "single") + except SyntaxError: + code = compile(line, "", "exec") + exec code in locals + +def evalPython(line, locals=None): + sandbox = S.Sandbox(config=createSandboxConfig()) + + if locals is not None: + locals = dict(locals) + else: + locals = dict() + try: + sandbox.call(_evalPython, line, locals) + except BaseException, e: + print 'Error: [%s] %s' % (e.__class__.__name__, str(e)) + except: + print 'Error: ' + sys.stdout.flush() + +def childProcess(line, w, locals): + # reseed after a fork to avoid generating the same sequence for each child + random.seed() + + sys.stdout = sys.stderr = os.fdopen(w, 'w') + + R.setrlimit(R.RLIMIT_CPU, (EVAL_MAXTIMESECONDS, EVAL_MAXTIMESECONDS)) + R.setrlimit(R.RLIMIT_AS, (EVAL_MAXMEMORYBYTES, EVAL_MAXMEMORYBYTES)) + R.setrlimit(R.RLIMIT_NPROC, (0, 0)) # 0 forks + + evalPython(line, locals) + +def handleChild(childpid, r): + txt = '' + if any(select.select([r], [], [], TIMEOUT)): + txt = os.read(r, OUT_MAXLEN + 1) + os.close(r) + if OUT_MAXLEN < len(txt): + txt = txt[:OUT_MAXLEN] + '...' + + n = 0 + while n < 6: + pid, status = os.waitpid(childpid, os.WNOHANG) + if pid: + break + time.sleep(.5) + n += 1 + if not pid: + os.kill(childpid, signal.SIGKILL) + return 'Timeout' + elif os.WIFEXITED(status): + txts = txt.rstrip().split('\n') + if len(txts) > 1: + txt = txts[0].rstrip() + ' [+ %d line(s)]' % (len(txts) - 1) + else: + txt = txts[0].rstrip() + return 'Output: ' + txt + elif os.WIFSIGNALED(status): + return 'Killed' + + + +class EvalJob(threading.Thread): + def __init__(self, line, irc, channel): + super(EvalJob, self).__init__() + self.line = line + self.irc = irc + self.channel = channel + + def run(self): + output = self.handle_line(self.line) + reactor.callFromThread(self.irc.say, self.channel, output) + self.irc.executionLock.release() + + def handle_line(self, line): + if IN_MAXLEN < len(line): + return '(command is too long: %s bytes, the maximum is %s)' % (len(line), IN_MAXLEN) + + print("Process %s" % repr(line)) + r, w = os.pipe() + childpid = os.fork() + if not childpid: + os.close(r) + childProcess(line, w, self.irc.factory.morevars) + os._exit(0) + else: + os.close(w) + result = handleChild(childpid, r) + print("=> %s" % repr(result)) + return result + + + +class EvalBot(IRCClient): + versionName = 'fschfsch' + versionNum = '0.1' + + #~ def __init__(self, *a, **k): + def connectionMade(self): + self.nickname = self.factory.nick + self.password = self.factory.password + self.talkre = re.compile('^%s[>:,] (.*)$' % self.nickname) + + self.executionLock = threading.Semaphore() + self.pingSelfId = None + + IRCClient.connectionMade(self) + + def signedOn(self): + self.pingSelfId = reactor.callLater(180, self.pingSelf) + for chan in self.factory.channels: + self.join(chan) + + def pingSelf(self): + # used to avoid some timeouts where fschfsch does not reconnect + self.ping(self.nickname) + self.pingSelfId = reactor.callLater(180, self.pingSelf) + + def privmsg(self, user, channel, message): + if self.pingSelfId is not None: + self.pingSelfId.reset(180) + if user.startswith('haypo') and message.startswith('exit'): + os._exit(0) + if not channel: + return + if not message.startswith(self.nickname): + return + if not self.talkre.match(message): + return + if not self.executionLock.acquire(blocking=False): + return + + pyline = self.talkre.match(message).group(1) + pyline = pyline.replace(' $$ ', '\n') + + self.handleThread = EvalJob(pyline, self, channel) + self.handleThread.start() + + +class MyFactory(ReconnectingClientFactory): + def __init__(self, **kw): + for k in kw: + if k in ('nick', 'password', 'channels', 'morevars'): + setattr(self, k, kw[k]) + protocol = EvalBot + +def check_output(expr, expected, locals=None): + from cStringIO import StringIO + original_stdout = sys.stdout + try: + output = StringIO() + sys.stdout = output + evalPython(expr, locals) + stdout = output.getvalue() + assert stdout == expected, "%r != %r" % (stdout, expected) + finally: + sys.stdout = original_stdout + +def runTests(): + # single + check_output('1+1', '2\n') + check_output('1; 2', '1\n2\n') + check_output( + # written in a single line + "prime=lambda n,i=2:" + "False if n%i==0 else prime(n,i+1) if i*i", "single") + except SyntaxError: + code = compile(line, "", "exec") + exec code in locals + +def evalPython(line, locals=None): + sandbox = S.Sandbox(config=createSandboxConfig()) + + if locals is not None: + locals = dict(locals) + else: + locals = dict() + try: + sandbox.call(_evalPython, line, locals) + except BaseException, e: + print 'Error: [%s] %s' % (e.__class__.__name__, str(e)) + except: + print 'Error: ' + sys.stdout.flush() + +def check_output(expr, expected, locals=None): + from cStringIO import StringIO + original_stdout = sys.stdout + try: + output = StringIO() + sys.stdout = output + evalPython(expr, locals) + stdout = output.getvalue() + assert stdout == expected, "%r != %r" % (stdout, expected) + finally: + sys.stdout = original_stdout + +def runTests(): + # single + check_output('1+1', '2\n') + check_output('1; 2', '1\n2\n') + check_output( + # written in a single line + "prime=lambda n,i=2:" + "False if n%i==0 else prime(n,i+1) if i*i.*)') + def sandbox(self, irc, msg, args): + """ + + Runs Python code safely thanks to pysandbox""" + code = self._parser.match(msg.args[1]).group('code') + irc.reply(handle_line(code.replace(' $$ ', '\n'))) + + def runtests(self, irc, msg, args): + irc.reply(runTests()) + + +Class = SupySandbox + + +# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/SupySandbox/test.py b/plugins/SupySandbox/test.py new file mode 100644 index 000000000..abf415165 --- /dev/null +++ b/plugins/SupySandbox/test.py @@ -0,0 +1,56 @@ +# -*- coding: 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. + +### + +from supybot.test import * + +class SupySandboxTestCase(PluginTestCase): + plugins = ('SupySandbox',) + + def testFschfschTestcase(self): + self.assertResponse('runtests', 'True') + + def testCodeIsSuccessfullyRunned(self): + self.assertResponse('sandbox 1+1', "2") + self.assertResponse('sandbox print 1+1', "2") + self.assertResponse('sandbox print \'toto\'', "toto") + + def testMultine(self): + self.assertResponse('sandbox print 1; print 2', "'1\\n2'") + self.assertResponse('sandbox print 1 $$ print 2', "'1\\n2'") + self.assertResponse('sandbox toto=True $$ while toto: $$ print "foo"' + ' $$ toto=False', "foo") + + def testProtections(self): + #self.assertResponse('sandbox while True: print 1', "Timeout") + pass + + +# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: From aaa0c480af0ddd9f36ef0ee37955e820ae33dff4 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Tue, 19 Oct 2010 19:50:41 +0200 Subject: [PATCH 034/136] Internationalize Relay, Reply, RSS, Scheduler --- plugins/RSS/config.py | 36 +++--- plugins/RSS/messages.pot | 164 +++++++++++++++++++++++++ plugins/RSS/plugin.py | 23 +++- plugins/Relay/config.py | 44 +++---- plugins/Relay/messages.pot | 215 +++++++++++++++++++++++++++++++++ plugins/Relay/plugin.py | 57 +++++---- plugins/Reply/config.py | 4 +- plugins/Reply/messages.pot | 65 ++++++++++ plugins/Reply/plugin.py | 7 ++ plugins/Scheduler/config.py | 4 +- plugins/Scheduler/messages.pot | 81 +++++++++++++ plugins/Scheduler/plugin.py | 18 ++- 12 files changed, 640 insertions(+), 78 deletions(-) create mode 100644 plugins/RSS/messages.pot create mode 100644 plugins/Relay/messages.pot create mode 100644 plugins/Reply/messages.pot create mode 100644 plugins/Scheduler/messages.pot diff --git a/plugins/RSS/config.py b/plugins/RSS/config.py index f36e9dc4b..e4754375b 100644 --- a/plugins/RSS/config.py +++ b/plugins/RSS/config.py @@ -30,6 +30,8 @@ import supybot.conf as conf import supybot.registry as registry import supybot.callbacks as callbacks +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('RSS') def configure(advanced): # This will be called by supybot to configure this module. advanced is @@ -45,37 +47,37 @@ class FeedNames(registry.SpaceSeparatedListOfStrings): RSS = conf.registerPlugin('RSS') conf.registerChannelValue(RSS, 'bold', registry.Boolean( - True, """Determines whether the bot will bold the title of the feed when it - announces new news.""")) + True, _("""Determines whether the bot will bold the title of the feed when + it announces new news."""))) conf.registerChannelValue(RSS, 'headlineSeparator', - registry.StringSurroundedBySpaces(' || ', """Determines what string is used - to separate headlines in new feeds.""")) + registry.StringSurroundedBySpaces(' || ', _("""Determines what string is + used to separate headlines in new feeds."""))) conf.registerChannelValue(RSS, 'announcementPrefix', - registry.StringWithSpaceOnRight('New news from ', """Determines what prefix - is prepended (if any) to the new news item announcements made in the - channel.""")) + registry.StringWithSpaceOnRight('New news from ', _("""Determines what + prefix is prepended (if any) to the new news item announcements made in the + channel."""))) conf.registerChannelValue(RSS, 'announce', - registry.SpaceSeparatedSetOfStrings([], """Determines which RSS feeds + registry.SpaceSeparatedSetOfStrings([], _("""Determines which RSS feeds should be announced in the channel; valid input is a list of strings - (either registered RSS feeds or RSS feed URLs) separated by spaces.""")) + (either registered RSS feeds or RSS feed URLs) separated by spaces."""))) conf.registerGlobalValue(RSS, 'waitPeriod', - registry.PositiveInteger(1800, """Indicates how many seconds the bot will + registry.PositiveInteger(1800, _("""Indicates how many seconds the bot will wait between retrieving RSS feeds; requests made within this period will - return cached results.""")) + return cached results."""))) conf.registerGlobalValue(RSS, 'feeds', - FeedNames([], """Determines what feeds should be accessible as - commands.""")) + FeedNames([], _("""Determines what feeds should be accessible as + commands."""))) conf.registerChannelValue(RSS, 'showLinks', - registry.Boolean(False, """Determines whether the bot will list the link + registry.Boolean(False, _("""Determines whether the bot will list the link along with the title of the feed when the rss command is called. supybot.plugins.RSS.announce.showLinks affects whether links will be - listed when a feed is automatically announced.""")) + listed when a feed is automatically announced."""))) conf.registerGroup(RSS, 'announce') conf.registerChannelValue(RSS.announce, 'showLinks', - registry.Boolean(False, """Determines whether the bot will list the link + registry.Boolean(False, _("""Determines whether the bot will list the link along with the title of the feed when a feed is automatically - announced.""")) + announced."""))) diff --git a/plugins/RSS/messages.pot b/plugins/RSS/messages.pot new file mode 100644 index 000000000..4a2b76686 --- /dev/null +++ b/plugins/RSS/messages.pot @@ -0,0 +1,164 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-19 19:27+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: config.py:50 +msgid "" +"Determines whether the bot will bold the title of the feed when it\n" +" announces new news." +msgstr "" + +#: config.py:53 +msgid "" +"Determines what string is used\n" +" to separate headlines in new feeds." +msgstr "" + +#: config.py:56 +msgid "" +"Determines what prefix\n" +" is prepended (if any) to the new news item announcements made in the\n" +" channel." +msgstr "" + +#: config.py:60 +msgid "" +"Determines which RSS feeds\n" +" should be announced in the channel; valid input is a list of strings\n" +" (either registered RSS feeds or RSS feed URLs) separated by spaces." +msgstr "" + +#: config.py:64 +msgid "" +"Indicates how many seconds the bot will\n" +" wait between retrieving RSS feeds; requests made within this period will\n" +" return cached results." +msgstr "" + +#: config.py:68 +msgid "" +"Determines what feeds should be accessible as\n" +" commands." +msgstr "" + +#: config.py:71 +msgid "" +"Determines whether the bot will list the link\n" +" along with the title of the feed when the rss command is called.\n" +" supybot.plugins.RSS.announce.showLinks affects whether links will be\n" +" listed when a feed is automatically announced." +msgstr "" + +#: config.py:78 +msgid "" +"Determines whether the bot will list the link\n" +" along with the title of the feed when a feed is automatically\n" +" announced." +msgstr "" + +#: plugin.py:63 +#, docstring +msgid "" +"This plugin is useful both for announcing updates to RSS feeds in a\n" +" channel, and for retrieving the headlines of RSS feeds via command. Use\n" +" the \"add\" command to add feeds to this plugin, and use the \"announce\"\n" +" command to determine what feeds should be announced in a given channel." +msgstr "" + +#: plugin.py:311 +#, docstring +msgid "" +" \n" +"\n" +" Adds a command to this plugin that will look up the RSS feed at the\n" +" given URL.\n" +" " +msgstr "" + +#: plugin.py:322 +#, docstring +msgid "" +"\n" +"\n" +" Removes the command for looking up RSS feeds at from\n" +" this plugin.\n" +" " +msgstr "" + +#: plugin.py:328 +msgid "That's not a valid RSS feed command name." +msgstr "" + +#: plugin.py:346 +msgid "I am currently not announcing any feeds." +msgstr "" + +#: plugin.py:351 +#, docstring +msgid "" +"[] [ ...]\n" +"\n" +" Adds the list of feeds to the current list of announced feeds in\n" +" . Valid feeds include the names of registered feeds as\n" +" well as URLs for RSS feeds. is only necessary if the\n" +" message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:369 +#, docstring +msgid "" +"[] [ ...]\n" +"\n" +" Removes the list of feeds from the current list of announced feeds\n" +" in . Valid feeds include the names of registered feeds as\n" +" well as URLs for RSS feeds. is only necessary if the\n" +" message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:387 +#, docstring +msgid "" +" []\n" +"\n" +" Gets the title components of the given RSS feed.\n" +" If is given, return only that many headlines.\n" +" " +msgstr "" + +#: plugin.py:400 +msgid "Couldn't get RSS feed." +msgstr "" + +#: plugin.py:413 +#, docstring +msgid "" +"\n" +"\n" +" Returns information from the given RSS feed, namely the title,\n" +" URL, description, and last update date, if available.\n" +" " +msgstr "" + +#: plugin.py:426 +msgid "I couldn't retrieve that RSS feed." +msgstr "" + +#: plugin.py:439 +msgid "Title: %s; URL: %u; Description: %s; Last updated: %s." +msgstr "" + diff --git a/plugins/RSS/plugin.py b/plugins/RSS/plugin.py index c5058d4ca..20e68bf6d 100644 --- a/plugins/RSS/plugin.py +++ b/plugins/RSS/plugin.py @@ -41,6 +41,8 @@ from supybot.commands import * import supybot.ircutils as ircutils import supybot.registry as registry import supybot.callbacks as callbacks +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('RSS') try: feedparser = utils.python.universalImport('feedparser', 'local.feedparser') @@ -56,6 +58,7 @@ def getFeedName(irc, msg, args, state): state.args.append(callbacks.canonicalName(args.pop(0))) addConverter('feedName', getFeedName) +@internationalizeDocstring class RSS(callbacks.Plugin): """This plugin is useful both for announcing updates to RSS feeds in a channel, and for retrieving the headlines of RSS feeds via command. Use @@ -280,6 +283,7 @@ class RSS(callbacks.Plugin): headlines.append((title, None)) return headlines + @internationalizeDocstring def makeFeedCommand(self, name, url): docstring = format("""[] @@ -302,6 +306,7 @@ class RSS(callbacks.Plugin): self.feedNames[name] = (url, f) self._registerFeed(name, url) + @internationalizeDocstring def add(self, irc, msg, args, name, url): """ @@ -312,6 +317,7 @@ class RSS(callbacks.Plugin): irc.replySuccess() add = wrap(add, ['feedName', 'url']) + @internationalizeDocstring def remove(self, irc, msg, args, name): """ @@ -319,7 +325,7 @@ class RSS(callbacks.Plugin): this plugin. """ if name not in self.feedNames: - irc.error('That\'s not a valid RSS feed command name.') + irc.error(_('That\'s not a valid RSS feed command name.')) return del self.feedNames[name] conf.supybot.plugins.RSS.feeds().remove(name) @@ -327,6 +333,7 @@ class RSS(callbacks.Plugin): irc.replySuccess() remove = wrap(remove, ['feedName']) + @internationalizeDocstring class announce(callbacks.Commands): def list(self, irc, msg, args, channel): """[] @@ -336,9 +343,10 @@ class RSS(callbacks.Plugin): """ announce = conf.supybot.plugins.RSS.announce feeds = format('%L', list(announce.get(channel)())) - irc.reply(feeds or 'I am currently not announcing any feeds.') + irc.reply(feeds or _('I am currently not announcing any feeds.')) list = wrap(list, ['channel',]) + @internationalizeDocstring def add(self, irc, msg, args, channel, feeds): """[] [ ...] @@ -356,6 +364,7 @@ class RSS(callbacks.Plugin): add = wrap(add, [('checkChannelCapability', 'op'), many(first('url', 'feedName'))]) + @internationalizeDocstring def remove(self, irc, msg, args, channel, feeds): """[] [ ...] @@ -373,6 +382,7 @@ class RSS(callbacks.Plugin): remove = wrap(remove, [('checkChannelCapability', 'op'), many(first('url', 'feedName'))]) + @internationalizeDocstring def rss(self, irc, msg, args, url, n): """ [] @@ -387,7 +397,7 @@ class RSS(callbacks.Plugin): channel = None headlines = self.getHeadlines(feed) if not headlines: - irc.error('Couldn\'t get RSS feed.') + irc.error(_('Couldn\'t get RSS feed.')) return headlines = self.buildHeadlines(headlines, channel, 'showLinks') if n: @@ -398,6 +408,7 @@ class RSS(callbacks.Plugin): irc.replies(headlines, joiner=sep) rss = wrap(rss, ['url', additional('int')]) + @internationalizeDocstring def info(self, irc, msg, args, url): """ @@ -412,7 +423,7 @@ class RSS(callbacks.Plugin): conv = self._getConverter(feed) info = feed.get('feed') if not info: - irc.error('I couldn\'t retrieve that RSS feed.') + irc.error(_('I couldn\'t retrieve that RSS feed.')) return # check the 'modified_parsed' key, if it's there, convert it here first if 'modified' in info: @@ -425,8 +436,8 @@ class RSS(callbacks.Plugin): desc = conv(info.get('description', 'unavailable')) link = conv(info.get('link', 'unavailable')) # The rest of the entries are all available in the channel key - response = format('Title: %s; URL: %u; ' - 'Description: %s; Last updated: %s.', + response = format(_('Title: %s; URL: %u; ' + 'Description: %s; Last updated: %s.'), title, link, desc, when) irc.reply(utils.str.normalizeWhitespace(response)) info = wrap(info, [first('url', 'feedName')]) diff --git a/plugins/Relay/config.py b/plugins/Relay/config.py index 757e42a9e..2bcbef095 100644 --- a/plugins/Relay/config.py +++ b/plugins/Relay/config.py @@ -30,14 +30,16 @@ import supybot.conf as conf import supybot.ircutils as ircutils import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Relay') def configure(advanced): from supybot.questions import output, expect, anything, something, yn conf.registerPlugin('Relay', True) - if yn('Would you like to relay between any channels?'): - channels = anything('What channels? Separated them by spaces.') + if yn(_('Would you like to relay between any channels?')): + channels = anything(_('What channels? Separated them by spaces.')) conf.supybot.plugins.Relay.channels.set(channels) - if yn('Would you like to use color to distinguish between nicks?'): + if yn(_('Would you like to use color to distinguish between nicks?')): conf.supybot.plugins.Relay.color.setValue(True) output("""Right now there's no way to configure the actual connection to the server. What you'll need to do when the bot finishes starting up is @@ -54,36 +56,36 @@ class Networks(registry.SpaceSeparatedListOf): Relay = conf.registerPlugin('Relay') conf.registerChannelValue(Relay, 'color', - registry.Boolean(False, """Determines whether the bot will color relayed - PRIVMSGs so as to make the messages easier to read.""")) + registry.Boolean(False, _("""Determines whether the bot will color relayed + PRIVMSGs so as to make the messages easier to read."""))) conf.registerChannelValue(Relay, 'topicSync', - registry.Boolean(True, """Determines whether the bot will synchronize - topics between networks in the channels it relays.""")) + registry.Boolean(True, _("""Determines whether the bot will synchronize + topics between networks in the channels it relays."""))) conf.registerChannelValue(Relay, 'hostmasks', - registry.Boolean(False, """Determines whether the bot will relay the + registry.Boolean(False, _("""Determines whether the bot will relay the hostmask of the person joining or parting the channel when he or she joins - or parts.""")) + or parts."""))) conf.registerChannelValue(Relay, 'includeNetwork', - registry.Boolean(True, """Determines whether the bot will include the + registry.Boolean(True, _("""Determines whether the bot will include the network in relayed PRIVMSGs; if you're only relaying between two networks, - it's somewhat redundant, and you may wish to save the space.""")) + it's somewhat redundant, and you may wish to save the space."""))) conf.registerChannelValue(Relay, 'punishOtherRelayBots', - registry.Boolean(False, """Determines whether the bot will detect other - bots relaying and respond by kickbanning them.""")) + registry.Boolean(False, _("""Determines whether the bot will detect other + bots relaying and respond by kickbanning them."""))) conf.registerGlobalValue(Relay, 'channels', - conf.SpaceSeparatedSetOfChannels([], """Determines which channels the bot - will relay in.""")) + conf.SpaceSeparatedSetOfChannels([], _("""Determines which channels the bot + will relay in."""))) conf.registerChannelValue(Relay.channels, 'joinOnAllNetworks', - registry.Boolean(True, """Determines whether the bot + registry.Boolean(True, _("""Determines whether the bot will always join the channel(s) it relays for on all networks the bot is - connected to.""")) + connected to."""))) conf.registerChannelValue(Relay, 'ignores', - Ignores([], """Determines what hostmasks will not be relayed on a - channel.""")) + Ignores([], _("""Determines what hostmasks will not be relayed on a + channel."""))) conf.registerChannelValue(Relay, 'noticeNonPrivmsgs', - registry.Boolean(False, """Determines whether the bot will used NOTICEs + registry.Boolean(False, _("""Determines whether the bot will used NOTICEs rather than PRIVMSGs for non-PRIVMSG relay messages (i.e., joins, parts, - nicks, quits, modes, etc.)""")) + nicks, quits, modes, etc.)"""))) # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/Relay/messages.pot b/plugins/Relay/messages.pot new file mode 100644 index 000000000..5757bcdc1 --- /dev/null +++ b/plugins/Relay/messages.pot @@ -0,0 +1,215 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-19 19:27+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: config.py:39 +msgid "Would you like to relay between any channels?" +msgstr "" + +#: config.py:40 +msgid "What channels? Separated them by spaces." +msgstr "" + +#: config.py:42 +msgid "Would you like to use color to distinguish between nicks?" +msgstr "" + +#: config.py:59 +msgid "" +"Determines whether the bot will color relayed\n" +" PRIVMSGs so as to make the messages easier to read." +msgstr "" + +#: config.py:62 +msgid "" +"Determines whether the bot will synchronize\n" +" topics between networks in the channels it relays." +msgstr "" + +#: config.py:65 +msgid "" +"Determines whether the bot will relay the\n" +" hostmask of the person joining or parting the channel when he or she joins\n" +" or parts." +msgstr "" + +#: config.py:69 +msgid "" +"Determines whether the bot will include the\n" +" network in relayed PRIVMSGs; if you're only relaying between two networks,\n" +" it's somewhat redundant, and you may wish to save the space." +msgstr "" + +#: config.py:73 +msgid "" +"Determines whether the bot will detect other\n" +" bots relaying and respond by kickbanning them." +msgstr "" + +#: config.py:76 +msgid "" +"Determines which channels the bot\n" +" will relay in." +msgstr "" + +#: config.py:79 +msgid "" +"Determines whether the bot\n" +" will always join the channel(s) it relays for on all networks the bot is\n" +" connected to." +msgstr "" + +#: config.py:83 +msgid "" +"Determines what hostmasks will not be relayed on a\n" +" channel." +msgstr "" + +#: config.py:86 +msgid "" +"Determines whether the bot will used NOTICEs\n" +" rather than PRIVMSGs for non-PRIVMSG relay messages (i.e., joins, parts,\n" +" nicks, quits, modes, etc.)" +msgstr "" + +#: plugin.py:99 +#, docstring +msgid "" +"[]\n" +"\n" +" Starts relaying between the channel on all networks. If on a\n" +" network the bot isn't in , he'll join. This commands is\n" +" required even if the bot is in the channel on both networks; he won't\n" +" relay between those channels unless he's told to join both\n" +" channels. If is not given, starts relaying on the channel\n" +" the message was sent in.\n" +" " +msgstr "" + +#: plugin.py:118 +#, docstring +msgid "" +"\n" +"\n" +" Ceases relaying between the channel on all networks. The bot\n" +" will part from the channel on all networks in which it is on the\n" +" channel.\n" +" " +msgstr "" + +#: plugin.py:133 +#, docstring +msgid "" +"[]\n" +"\n" +" Returns the nicks of the people in the channel on the various networks\n" +" the bot is connected to. is only necessary if the message\n" +" isn't sent on the channel itself.\n" +" " +msgstr "" + +#: plugin.py:223 +msgid "is an op on %L" +msgstr "" + +#: plugin.py:225 +msgid "is a halfop on %L" +msgstr "" + +#: plugin.py:227 +msgid "is voiced on %L" +msgstr "" + +#: plugin.py:230 +msgid "is also on %L" +msgstr "" + +#: plugin.py:232 +msgid "is on %L" +msgstr "" + +#: plugin.py:234 +msgid "isn't on any non-secret channels" +msgstr "" + +#: plugin.py:241 plugin.py:242 plugin.py:246 +msgid "" +msgstr "" + +#: plugin.py:248 +msgid " %s is away: %s." +msgstr "" + +#: plugin.py:253 +msgid " identified" +msgstr "" + +#: plugin.py:258 +msgid "%s (%s) has been%s on server %s since %s (idle for %s) and %s.%s" +msgstr "" + +#: plugin.py:273 +msgid "There is no %s on %s." +msgstr "" + +#: plugin.py:342 +msgid "You seem to be relaying, punk." +msgstr "" + +#: plugin.py:395 +msgid "%s%s has joined on %s" +msgstr "" + +#: plugin.py:410 +msgid "%s%s has left on %s (%s)" +msgstr "" + +#: plugin.py:413 +msgid "%s%s has left on %s" +msgstr "" + +#: plugin.py:423 +msgid "mode change by %s on %s: %s" +msgstr "" + +#: plugin.py:435 +msgid "%s was kicked by %s on %s (%s)" +msgstr "" + +#: plugin.py:438 +msgid "%s was kicked by %s on %s" +msgstr "" + +#: plugin.py:447 +msgid "nick change by %s to %s on %s" +msgstr "" + +#: plugin.py:477 +msgid "topic change by %s on %s: %s" +msgstr "" + +#: plugin.py:486 +msgid "%s has quit %s (%s)" +msgstr "" + +#: plugin.py:488 +msgid "%s has quit %s." +msgstr "" + +#: plugin.py:498 +msgid "disconnected from %s: %s" +msgstr "" + diff --git a/plugins/Relay/plugin.py b/plugins/Relay/plugin.py index ffd31f80f..6fe419589 100644 --- a/plugins/Relay/plugin.py +++ b/plugins/Relay/plugin.py @@ -39,6 +39,8 @@ import supybot.ircmsgs as ircmsgs import supybot.ircutils as ircutils import supybot.callbacks as callbacks from supybot.utils.structures import MultiSet, TimeoutQueue +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Relay') class Relay(callbacks.Plugin): noIgnore = True @@ -92,6 +94,7 @@ class Relay(callbacks.Plugin): irc.queueMsg(ircmsgs.who(channel)) irc.queueMsg(ircmsgs.names(channel)) + @internationalizeDocstring def join(self, irc, msg, args, channel): """[] @@ -110,6 +113,7 @@ class Relay(callbacks.Plugin): irc.replySuccess() join = wrap(join, ['channel', 'admin']) + @internationalizeDocstring def part(self, irc, msg, args, channel): """ @@ -124,6 +128,7 @@ class Relay(callbacks.Plugin): irc.replySuccess() part = wrap(part, ['channel', 'admin']) + @internationalizeDocstring def nicks(self, irc, msg, args, channel): """[] @@ -215,43 +220,43 @@ class Relay(callbacks.Plugin): normal.append(channel) L = [] if ops: - L.append(format('is an op on %L', ops)) + L.append(format(_('is an op on %L'), ops)) if halfops: - L.append(format('is a halfop on %L', halfups)) + L.append(format(_('is a halfop on %L'), halfups)) if voices: - L.append(format('is voiced on %L', voices)) + L.append(format(_('is voiced on %L'), voices)) if normal: if L: - L.append(format('is also on %L', normal)) + L.append(format(_('is also on %L'), normal)) else: - L.append(format('is on %L', normal)) + L.append(format(_('is on %L'), normal)) else: - L = ['isn\'t on any non-secret channels'] + L = [_('isn\'t on any non-secret channels')] channels = format('%L', L) if '317' in d: idle = utils.timeElapsed(d['317'].args[2]) signon = time.strftime(conf.supybot.reply.format.time(), time.localtime(float(d['317'].args[3]))) else: - idle = '' - signon = '' + idle = _('') + signon = _('') if '312' in d: server = d['312'].args[2] else: - server = '' + server = _('') if '301' in d: - away = format(' %s is away: %s.', nick, d['301'].args[2]) + away = format(_(' %s is away: %s.'), nick, d['301'].args[2]) else: away = '' if '320' in d: if d['320'].args[2]: - identify = ' identified' + identify = _(' identified') else: identify = '' else: identify = '' - s = format('%s (%s) has been%s on server %s since %s (idle for %s) ' - 'and %s.%s', + s = format(_('%s (%s) has been%s on server %s since %s (idle for %s) ' + 'and %s.%s'), user, hostmask, identify, server, signon, idle, channels, away) replyIrc.reply(s) @@ -265,7 +270,7 @@ class Relay(callbacks.Plugin): return (replyIrc, replyMsg, d) = self._whois[(irc, loweredNick)] del self._whois[(irc, loweredNick)] - s = format('There is no %s on %s.', nick, self._getIrcName(irc)) + s = format(_('There is no %s on %s.'), nick, self._getIrcName(irc)) replyIrc.reply(s) do401 = do402 @@ -334,7 +339,7 @@ class Relay(callbacks.Plugin): self.log.info('Punishing %s in %s on %s for relaying.', who, channel, irc.network) irc.sendMsg(ircmsgs.ban(channel, who)) - kmsg = 'You seem to be relaying, punk.' + kmsg = _('You seem to be relaying, punk.') irc.sendMsg(ircmsgs.kick(channel, msg.nick, kmsg)) else: notPunishing(irc, 'not opped') @@ -387,7 +392,7 @@ class Relay(callbacks.Plugin): hostmask = format(' (%s)', msg.prefix) else: hostmask = '' - s = format('%s%s has joined on %s', msg.nick, hostmask, network) + s = format(_('%s%s has joined on %s'), msg.nick, hostmask, network) m = self._msgmaker(channel, s) self._sendToOthers(irc, m) @@ -402,10 +407,10 @@ class Relay(callbacks.Plugin): else: hostmask = '' if len(msg.args) > 1: - s = format('%s%s has left on %s (%s)', + s = format(_('%s%s has left on %s (%s)'), msg.nick, hostmask, network, msg.args[1]) else: - s = format('%s%s has left on %s', msg.nick, hostmask, network) + s = format(_('%s%s has left on %s'), msg.nick, hostmask, network) m = self._msgmaker(channel, s) self._sendToOthers(irc, m) @@ -415,7 +420,7 @@ class Relay(callbacks.Plugin): if channel not in self.registryValue('channels'): return network = self._getIrcName(irc) - s = format('mode change by %s on %s: %s', + s = format(_('mode change by %s on %s: %s'), msg.nick, network, ' '.join(msg.args[1:])) m = self._msgmaker(channel, s) self._sendToOthers(irc, m) @@ -427,10 +432,10 @@ class Relay(callbacks.Plugin): return network = self._getIrcName(irc) if len(msg.args) == 3: - s = format('%s was kicked by %s on %s (%s)', + s = format(_('%s was kicked by %s on %s (%s)'), msg.args[1], msg.nick, network, msg.args[2]) else: - s = format('%s was kicked by %s on %s', + s = format(_('%s was kicked by %s on %s'), msg.args[1], msg.nick, network) m = self._msgmaker(channel, s) self._sendToOthers(irc, m) @@ -439,7 +444,7 @@ class Relay(callbacks.Plugin): irc = self._getRealIrc(irc) newNick = msg.args[0] network = self._getIrcName(irc) - s = format('nick change by %s to %s on %s', msg.nick,newNick,network) + s = format(_('nick change by %s to %s on %s'), msg.nick,newNick,network) for channel in self.registryValue('channels'): if channel in irc.state.channels: if newNick in irc.state.channels[channel].users: @@ -469,7 +474,7 @@ class Relay(callbacks.Plugin): 'can\'t sync topics.', channel, otherIrc.network) else: - s = format('topic change by %s on %s: %s', + s = format(_('topic change by %s on %s: %s'), msg.nick, network, newTopic) m = self._msgmaker(channel, s) self._sendToOthers(irc, m) @@ -478,9 +483,9 @@ class Relay(callbacks.Plugin): irc = self._getRealIrc(irc) network = self._getIrcName(irc) if msg.args: - s = format('%s has quit %s (%s)', msg.nick, network, msg.args[0]) + s = format(_('%s has quit %s (%s)'), msg.nick, network, msg.args[0]) else: - s = format('%s has quit %s.', msg.nick, network) + s = format(_('%s has quit %s.'), msg.nick, network) for channel in self.registryValue('channels'): if channel in self.ircstates[irc].channels: if msg.nick in self.ircstates[irc].channels[channel].users: @@ -490,7 +495,7 @@ class Relay(callbacks.Plugin): def doError(self, irc, msg): irc = self._getRealIrc(irc) network = self._getIrcName(irc) - s = format('disconnected from %s: %s', network, msg.args[0]) + s = format(_('disconnected from %s: %s'), network, msg.args[0]) for channel in self.registryValue('channels'): m = self._msgmaker(channel, s) self._sendToOthers(irc, m) diff --git a/plugins/Reply/config.py b/plugins/Reply/config.py index 7d861c528..f094cb36f 100644 --- a/plugins/Reply/config.py +++ b/plugins/Reply/config.py @@ -29,6 +29,8 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Reply') def configure(advanced): # This will be called by supybot to configure this module. advanced is @@ -42,7 +44,7 @@ def configure(advanced): Reply = conf.registerPlugin('Reply') # This is where your configuration variables (if any) should go. For example: # conf.registerGlobalValue(Reply, 'someConfigVariableName', -# registry.Boolean(False, """Help for someConfigVariableName.""")) +# registry.Boolean(False, _("""Help for someConfigVariableName."""))) # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/Reply/messages.pot b/plugins/Reply/messages.pot new file mode 100644 index 000000000..fac59c514 --- /dev/null +++ b/plugins/Reply/messages.pot @@ -0,0 +1,65 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-19 19:27+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: plugin.py:38 +#, docstring +msgid "" +"This plugins contains a few commands that construct various types of\n" +" replies. Some bot owners would be wise to not load this plugin because it\n" +" can be easily abused.\n" +" " +msgstr "" + +#: plugin.py:44 +#, docstring +msgid "" +"\n" +"\n" +" Replies with in private. Use nested commands to your benefit\n" +" here.\n" +" " +msgstr "" + +#: plugin.py:54 +#, docstring +msgid "" +"\n" +"\n" +" Replies with as an action. use nested commands to your benefit\n" +" here.\n" +" " +msgstr "" + +#: plugin.py:67 +#, docstring +msgid "" +"\n" +"\n" +" Replies with in a notice. Use nested commands to your benefit\n" +" here. If you want a private notice, nest the private command.\n" +" " +msgstr "" + +#: plugin.py:77 +#, docstring +msgid "" +"\n" +"\n" +" Replies with . Equivalent to the alias, 'echo $nick: $1'.\n" +" " +msgstr "" + diff --git a/plugins/Reply/plugin.py b/plugins/Reply/plugin.py index 9b0a6db4a..4bee7225f 100644 --- a/plugins/Reply/plugin.py +++ b/plugins/Reply/plugin.py @@ -29,13 +29,17 @@ from supybot.commands import * import supybot.callbacks as callbacks +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Reply') +@internationalizeDocstring class Reply(callbacks.Plugin): """This plugins contains a few commands that construct various types of replies. Some bot owners would be wise to not load this plugin because it can be easily abused. """ + @internationalizeDocstring def private(self, irc, msg, args, text): """ @@ -45,6 +49,7 @@ class Reply(callbacks.Plugin): irc.reply(text, private=True) private = wrap(private, ['text']) + @internationalizeDocstring def action(self, irc, msg, args, text): """ @@ -57,6 +62,7 @@ class Reply(callbacks.Plugin): raise callbacks.ArgumentError action = wrap(action, ['text']) + @internationalizeDocstring def notice(self, irc, msg, args, text): """ @@ -66,6 +72,7 @@ class Reply(callbacks.Plugin): irc.reply(text, notice=True) notice = wrap(notice, ['text']) + @internationalizeDocstring def reply(self, irc, msg, args, text): """ diff --git a/plugins/Scheduler/config.py b/plugins/Scheduler/config.py index bd901402b..9943c4557 100644 --- a/plugins/Scheduler/config.py +++ b/plugins/Scheduler/config.py @@ -29,6 +29,8 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Scheduler') def configure(advanced): # This will be called by supybot to configure this module. advanced is @@ -42,7 +44,7 @@ def configure(advanced): Scheduler = conf.registerPlugin('Scheduler') # This is where your configuration variables (if any) should go. For example: # conf.registerGlobalValue(Scheduler, 'someConfigVariableName', -# registry.Boolean(False, """Help for someConfigVariableName.""")) +# registry.Boolean(False, _("""Help for someConfigVariableName."""))) # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/Scheduler/messages.pot b/plugins/Scheduler/messages.pot new file mode 100644 index 000000000..226ac4fe6 --- /dev/null +++ b/plugins/Scheduler/messages.pot @@ -0,0 +1,81 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-19 19:28+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: plugin.py:47 +#, docstring +msgid "Makes a function suitable for scheduling from command." +msgstr "" + +#: plugin.py:57 +#, docstring +msgid "" +" \n" +"\n" +" Schedules the command string to run seconds in the\n" +" future. For example, 'scheduler add [seconds 30m] \"echo [cpu]\"' will\n" +" schedule the command \"cpu\" to be sent to the channel the schedule add\n" +" command was given in (with no prefixed nick, a consequence of using\n" +" echo). Do pay attention to the quotes in that example.\n" +" " +msgstr "" + +#: plugin.py:69 +msgid "Event #%i added." +msgstr "" + +#: plugin.py:74 +#, docstring +msgid "" +"\n" +"\n" +" Removes the event scheduled with id from the schedule.\n" +" " +msgstr "" + +#: plugin.py:88 plugin.py:90 +msgid "Invalid event id." +msgstr "" + +#: plugin.py:95 +#, docstring +msgid "" +" \n" +"\n" +" Schedules the command to run every seconds,\n" +" starting now (i.e., the command runs now, and every seconds\n" +" thereafter). is a name by which the command can be\n" +" unscheduled.\n" +" " +msgstr "" + +#: plugin.py:104 +msgid "There is already an event with that name, please choose another name." +msgstr "" + +#: plugin.py:117 +#, docstring +msgid "" +"takes no arguments\n" +"\n" +" Lists the currently scheduled events.\n" +" " +msgstr "" + +#: plugin.py:128 +msgid "There are currently no scheduled commands." +msgstr "" + diff --git a/plugins/Scheduler/plugin.py b/plugins/Scheduler/plugin.py index 5ad55dc7b..ec1dd77af 100644 --- a/plugins/Scheduler/plugin.py +++ b/plugins/Scheduler/plugin.py @@ -34,6 +34,8 @@ import supybot.utils as utils from supybot.commands import * import supybot.schedule as schedule import supybot.callbacks as callbacks +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Scheduler') class Scheduler(callbacks.Plugin): def __init__(self, irc): @@ -50,6 +52,7 @@ class Scheduler(callbacks.Plugin): self.Proxy(irc.irc, msg, tokens) return f + @internationalizeDocstring def add(self, irc, msg, args, seconds, command): """ @@ -63,9 +66,10 @@ class Scheduler(callbacks.Plugin): id = schedule.addEvent(f, time.time() + seconds) f.eventId = id self.events[str(id)] = command - irc.replySuccess(format('Event #%i added.', id)) + irc.replySuccess(format(_('Event #%i added.'), id)) add = wrap(add, ['positiveInt', 'text']) + @internationalizeDocstring def remove(self, irc, msg, args, id): """ @@ -81,11 +85,12 @@ class Scheduler(callbacks.Plugin): schedule.removeEvent(id) irc.replySuccess() except KeyError: - irc.error('Invalid event id.') + irc.error(_('Invalid event id.')) else: - irc.error('Invalid event id.') + irc.error(_('Invalid event id.')) remove = wrap(remove, ['lowered']) + @internationalizeDocstring def repeat(self, irc, msg, args, name, seconds, command): """ @@ -96,8 +101,8 @@ class Scheduler(callbacks.Plugin): """ name = name.lower() if name in self.events: - irc.error('There is already an event with that name, please ' - 'choose another name.', Raise=True) + irc.error(_('There is already an event with that name, please ' + 'choose another name.'), Raise=True) self.events[name] = command f = self._makeCommandFunction(irc, msg, command, remove=False) id = schedule.addPeriodicEvent(f, seconds, name) @@ -107,6 +112,7 @@ class Scheduler(callbacks.Plugin): # irc.replySuccess() repeat = wrap(repeat, ['nonInt', 'positiveInt', 'text']) + @internationalizeDocstring def list(self, irc, msg, args): """takes no arguments @@ -119,7 +125,7 @@ class Scheduler(callbacks.Plugin): L[i] = format('%s: %q', name, command) irc.reply(format('%L', L)) else: - irc.reply('There are currently no scheduled commands.') + irc.reply(_('There are currently no scheduled commands.')) list = wrap(list) From 371a40e0047519546d2f32dd9b8fb2fe609d9392 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Wed, 20 Oct 2010 09:10:03 +0200 Subject: [PATCH 035/136] Internationalize Seen, Services, ShrinkUrl, Status, String, and Success --- plugins/Seen/config.py | 4 +- plugins/Seen/messages.pot | 118 +++++++++++++++++ plugins/Seen/plugin.py | 36 +++-- plugins/Services/config.py | 54 ++++---- plugins/Services/messages.pot | 235 +++++++++++++++++++++++++++++++++ plugins/Services/plugin.py | 37 ++++-- plugins/ShrinkUrl/config.py | 40 +++--- plugins/ShrinkUrl/messages.pot | 119 +++++++++++++++++ plugins/ShrinkUrl/plugin.py | 6 + plugins/Status/config.py | 14 +- plugins/Status/messages.pot | 155 ++++++++++++++++++++++ plugins/Status/plugin.py | 46 ++++--- plugins/String/config.py | 6 +- plugins/String/messages.pot | 168 +++++++++++++++++++++++ plugins/String/plugin.py | 31 +++-- plugins/Success/config.py | 8 +- plugins/Success/messages.pot | 33 +++++ plugins/Success/plugin.py | 3 + 18 files changed, 1002 insertions(+), 111 deletions(-) create mode 100644 plugins/Seen/messages.pot create mode 100644 plugins/Services/messages.pot create mode 100644 plugins/ShrinkUrl/messages.pot create mode 100644 plugins/Status/messages.pot create mode 100644 plugins/String/messages.pot create mode 100644 plugins/Success/messages.pot diff --git a/plugins/Seen/config.py b/plugins/Seen/config.py index fa24b305d..d7111bf93 100644 --- a/plugins/Seen/config.py +++ b/plugins/Seen/config.py @@ -29,6 +29,8 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Seen') def configure(advanced): # This will be called by supybot to configure this module. advanced is @@ -42,7 +44,7 @@ def configure(advanced): Seen = conf.registerPlugin('Seen') # This is where your configuration variables (if any) should go. For example: # conf.registerGlobalValue(Seen, 'someConfigVariableName', -# registry.Boolean(False, """Help for someConfigVariableName.""")) +# registry.Boolean(False, _("""Help for someConfigVariableName."""))) # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/Seen/messages.pot b/plugins/Seen/messages.pot new file mode 100644 index 000000000..e1186b0f1 --- /dev/null +++ b/plugins/Seen/messages.pot @@ -0,0 +1,118 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-20 08:52+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: plugin.py:203 plugin.py:284 +msgid "%s was last seen in %s %s ago: %s" +msgstr "" + +#: plugin.py:210 +msgid "%s (%s ago)" +msgstr "" + +#: plugin.py:212 +msgid "%s could be %L" +msgstr "" + +#: plugin.py:212 +msgid "or" +msgstr "" + +#: plugin.py:214 +msgid "I haven't seen anyone matching %s." +msgstr "" + +#: plugin.py:216 plugin.py:288 +msgid "I have not seen %s." +msgstr "" + +#: plugin.py:220 +#, docstring +msgid "" +"[] \n" +"\n" +" Returns the last time was seen and what was last seen\n" +" saying. is only necessary if the message isn't sent on the\n" +" channel itself.\n" +" " +msgstr "" + +#: plugin.py:231 +#, docstring +msgid "" +"[] [--user ] []\n" +"\n" +" Returns the last time was seen and what was last seen\n" +" doing. This includes any form of activity, instead of just PRIVMSGs.\n" +" If isn't specified, returns the last activity seen in\n" +" . If --user is specified, looks up name in the user database\n" +" and returns the last time user was active in . is\n" +" only necessary if the message isn't sent on the channel itself.\n" +" " +msgstr "" + +#: plugin.py:261 +msgid "Someone was last seen in %s %s ago: %s" +msgstr "" + +#: plugin.py:265 +msgid "I have never seen anyone." +msgstr "" + +#: plugin.py:269 +#, docstring +msgid "" +"[]\n" +"\n" +" Returns the last thing said in . is only necessary\n" +" if the message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:292 +#, docstring +msgid "" +"[] \n" +"\n" +" Returns the last time was seen and what was last seen\n" +" saying. This looks up in the user seen database, which means\n" +" that it could be any nick recognized as user that was seen.\n" +" is only necessary if the message isn't sent in the channel\n" +" itself.\n" +" " +msgstr "" + +#: plugin.py:305 +#, docstring +msgid "" +"[] \n" +"\n" +" Returns the messages since last left the channel.\n" +" " +msgstr "" + +#: plugin.py:312 +msgid "You must be in %s to use this command." +msgstr "" + +#: plugin.py:333 +msgid "I couldn't find in my history of %s messages where %r last left the %s" +msgstr "" + +#: plugin.py:342 +msgid "Either %s didn't leave, or no messages were sent while %s was gone." +msgstr "" + diff --git a/plugins/Seen/plugin.py b/plugins/Seen/plugin.py index 4051e0994..7d2fee947 100644 --- a/plugins/Seen/plugin.py +++ b/plugins/Seen/plugin.py @@ -42,6 +42,8 @@ import supybot.ircmsgs as ircmsgs import supybot.plugins as plugins import supybot.ircutils as ircutils import supybot.callbacks as callbacks +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Seen') class IrcStringAndIntDict(utils.InsensitivePreservingDict): def key(self, x): @@ -198,21 +200,22 @@ class Seen(callbacks.Plugin): if len(results) == 1: (nick, info) = results[0] (when, said) = info - irc.reply(format('%s was last seen in %s %s ago: %s', + irc.reply(format(_('%s was last seen in %s %s ago: %s'), nick, channel, utils.timeElapsed(time.time()-when), said)) elif len(results) > 1: L = [] for (nick, info) in results: (when, said) = info - L.append(format('%s (%s ago)', nick, + L.append(format(_('%s (%s ago)'), nick, utils.timeElapsed(time.time()-when))) - irc.reply(format('%s could be %L', name, (L, 'or'))) + irc.reply(format(_('%s could be %L'), name, (L, _('or')))) else: - irc.reply(format('I haven\'t seen anyone matching %s.', name)) + irc.reply(format(_('I haven\'t seen anyone matching %s.'), name)) except KeyError: - irc.reply(format('I have not seen %s.', name)) + irc.reply(format(_('I have not seen %s.'), name)) + @internationalizeDocstring def seen(self, irc, msg, args, channel, name): """[] @@ -223,6 +226,7 @@ class Seen(callbacks.Plugin): self._seen(irc, channel, name) seen = wrap(seen, ['channel', 'nick']) + @internationalizeDocstring def any(self, irc, msg, args, channel, optlist, name): """[] [--user ] [] @@ -254,12 +258,13 @@ class Seen(callbacks.Plugin): db = self.db try: (when, said) = db.seen(channel, '') - irc.reply(format('Someone was last seen in %s %s ago: %s', + irc.reply(format(_('Someone was last seen in %s %s ago: %s'), channel, utils.timeElapsed(time.time()-when), said)) except KeyError: - irc.reply('I have never seen anyone.') + irc.reply(_('I have never seen anyone.')) + @internationalizeDocstring def last(self, irc, msg, args, channel): """[] @@ -276,12 +281,13 @@ class Seen(callbacks.Plugin): db = self.db try: (when, said) = db.seen(channel, user.id) - irc.reply(format('%s was last seen in %s %s ago: %s', + irc.reply(format(_('%s was last seen in %s %s ago: %s'), user.name, channel, utils.timeElapsed(time.time()-when), said)) except KeyError: - irc.reply(format('I have not seen %s.', user.name)) + irc.reply(format(_('I have not seen %s.'), user.name)) + @internationalizeDocstring def user(self, irc, msg, args, channel, user): """[] @@ -294,6 +300,7 @@ class Seen(callbacks.Plugin): self._user(irc, channel, user) user = wrap(user, ['channel', 'otherUser']) + @internationalizeDocstring def since(self, irc, msg, args, channel, nick): """[] @@ -302,7 +309,8 @@ class Seen(callbacks.Plugin): if nick is None: nick = msg.nick if nick not in irc.state.channels[channel].users: - irc.error(format('You must be in %s to use this command.', channel)) + irc.error(format(_('You must be in %s to use this command.'), + channel)) return end = None # By default, up until the most recent message. for (i, m) in utils.seq.renumerate(irc.state.history): @@ -322,8 +330,8 @@ class Seen(callbacks.Plugin): ircutils.strEqual(m.args[0], channel): break else: # I never use this; it only kicks in when the for loop exited normally. - irc.error(format('I couldn\'t find in my history of %s messages ' - 'where %r last left the %s', + irc.error(format(_('I couldn\'t find in my history of %s messages ' + 'where %r last left the %s'), len(irc.state.history), nick, channel)) return msgs = [m for m in irc.state.history[i:end] @@ -331,8 +339,8 @@ class Seen(callbacks.Plugin): if msgs: irc.reply(format('%L', map(ircmsgs.prettyPrint, msgs))) else: - irc.reply(format('Either %s didn\'t leave, ' - 'or no messages were sent while %s was gone.', nick, nick)) + irc.reply(format(_('Either %s didn\'t leave, ' + 'or no messages were sent while %s was gone.'), nick, nick)) since = wrap(since, ['channel', additional('nick')]) Class = Seen diff --git a/plugins/Services/config.py b/plugins/Services/config.py index aa09131ea..ffb2db783 100644 --- a/plugins/Services/config.py +++ b/plugins/Services/config.py @@ -31,11 +31,13 @@ import supybot.conf as conf import supybot.ircutils as ircutils import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Services') def registerNick(nick, password=''): p = conf.supybot.plugins.Services.Nickserv.get('password') - h = 'Determines what password the bot will use with NickServ when ' \ - 'identifying as %s.' % nick + h = _('Determines what password the bot will use with NickServ when ' \ + 'identifying as %s.') % nick v = conf.registerGlobalValue(p, nick, registry.String(password, h, private=True)) if password: @@ -44,10 +46,10 @@ def registerNick(nick, password=''): def configure(advanced): from supybot.questions import expect, anything, something, yn, getpass conf.registerPlugin('Services', True) - nick = something('What is your registered nick?') - password = something('What is your password for that nick?') - chanserv = something('What is your ChanServ named?', default='ChanServ') - nickserv = something('What is your NickServ named?', default='NickServ') + nick = something(_('What is your registered nick?')) + password = something(_('What is your password for that nick?')) + chanserv = something(_('What is your ChanServ named?'), default='ChanServ') + nickserv = something(_('What is your NickServ named?'), default='NickServ') conf.supybot.plugins.Services.nicks.setValue([nick]) conf.supybot.plugins.Services.NickServ.setValue(nickserv) registerNick(nick, password) @@ -65,42 +67,42 @@ class ValidNickSet(conf.ValidNicks): Services = conf.registerPlugin('Services') conf.registerGlobalValue(Services, 'nicks', - ValidNickSet([], """Determines what nicks the bot will use with - services.""")) + ValidNickSet([], _("""Determines what nicks the bot will use with + services."""))) class Networks(registry.SpaceSeparatedSetOfStrings): List = ircutils.IrcSet conf.registerGlobalValue(Services, 'disabledNetworks', - Networks(['QuakeNet'], """Determines what networks this plugin will be - disabled on.""")) + Networks(_('QuakeNet').split(), _("""Determines what networks this plugin + will be disabled on."""))) conf.registerGlobalValue(Services, 'noJoinsUntilIdentified', - registry.Boolean(False, """Determines whether the bot will not join any + registry.Boolean(False, _("""Determines whether the bot will not join any channels until it is identified. This may be useful, for instances, if you have a vhost that isn't set until you're identified, or if you're - joining +r channels that won't allow you to join unless you identify.""")) + joining +r channels that won't allow you to join unless you identify."""))) conf.registerGlobalValue(Services, 'ghostDelay', - registry.PositiveInteger(60, """Determines how many seconds the bot will - wait between successive GHOST attempts.""")) + registry.PositiveInteger(60, _("""Determines how many seconds the bot will + wait between successive GHOST attempts."""))) conf.registerGlobalValue(Services, 'NickServ', - ValidNickOrEmptyString('', """Determines what nick the 'NickServ' service - has.""")) + ValidNickOrEmptyString('', _("""Determines what nick the 'NickServ' service + has."""))) conf.registerGroup(Services.NickServ, 'password') conf.registerGlobalValue(Services, 'ChanServ', - ValidNickOrEmptyString('', """Determines what nick the 'ChanServ' service - has.""")) + ValidNickOrEmptyString('', _("""Determines what nick the 'ChanServ' service + has."""))) conf.registerChannelValue(Services.ChanServ, 'password', - registry.String('', """Determines what password the bot will use with - ChanServ.""", private=True)) + registry.String('', _("""Determines what password the bot will use with + ChanServ."""), private=True)) conf.registerChannelValue(Services.ChanServ, 'op', - registry.Boolean(False, """Determines whether the bot will request to get - opped by the ChanServ when it joins the channel.""")) + registry.Boolean(False, _("""Determines whether the bot will request to get + opped by the ChanServ when it joins the channel."""))) conf.registerChannelValue(Services.ChanServ, 'halfop', - registry.Boolean(False, """Determines whether the bot will request to get - half-opped by the ChanServ when it joins the channel.""")) + registry.Boolean(False, _("""Determines whether the bot will request to get + half-opped by the ChanServ when it joins the channel."""))) conf.registerChannelValue(Services.ChanServ, 'voice', - registry.Boolean(False, """Determines whether the bot will request to get - voiced by the ChanServ when it joins the channel.""")) + registry.Boolean(False, _("""Determines whether the bot will request to get + voiced by the ChanServ when it joins the channel."""))) # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/Services/messages.pot b/plugins/Services/messages.pot new file mode 100644 index 000000000..05b81d37a --- /dev/null +++ b/plugins/Services/messages.pot @@ -0,0 +1,235 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-20 08:55+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: config.py:39 +msgid "Determines what password the bot will use with NickServ when identifying as %s." +msgstr "" + +#: config.py:49 +msgid "What is your registered nick?" +msgstr "" + +#: config.py:50 +msgid "What is your password for that nick?" +msgstr "" + +#: config.py:51 +msgid "What is your ChanServ named?" +msgstr "" + +#: config.py:52 +msgid "What is your NickServ named?" +msgstr "" + +#: config.py:70 +msgid "" +"Determines what nicks the bot will use with\n" +" services." +msgstr "" + +#: config.py:77 +msgid "" +"Determines what networks this plugin\n" +" will be disabled on." +msgstr "" + +#: config.py:77 +msgid "QuakeNet" +msgstr "" + +#: config.py:81 +msgid "" +"Determines whether the bot will not join any\n" +" channels until it is identified. This may be useful, for instances, if\n" +" you have a vhost that isn't set until you're identified, or if you're\n" +" joining +r channels that won't allow you to join unless you identify." +msgstr "" + +#: config.py:86 +msgid "" +"Determines how many seconds the bot will\n" +" wait between successive GHOST attempts." +msgstr "" + +#: config.py:89 +msgid "" +"Determines what nick the 'NickServ' service\n" +" has." +msgstr "" + +#: config.py:93 +msgid "" +"Determines what nick the 'ChanServ' service\n" +" has." +msgstr "" + +#: config.py:96 +msgid "" +"Determines what password the bot will use with\n" +" ChanServ." +msgstr "" + +#: config.py:99 +msgid "" +"Determines whether the bot will request to get\n" +" opped by the ChanServ when it joins the channel." +msgstr "" + +#: config.py:102 +msgid "" +"Determines whether the bot will request to get\n" +" half-opped by the ChanServ when it joins the channel." +msgstr "" + +#: config.py:105 +msgid "" +"Determines whether the bot will request to get\n" +" voiced by the ChanServ when it joins the channel." +msgstr "" + +#: plugin.py:48 +#, docstring +msgid "" +"This plugin handles dealing with Services on networks that provide them.\n" +" Basically, you should use the \"password\" command to tell the bot a nick to\n" +" identify with and what password to use to identify with that nick. You can\n" +" use the password command multiple times if your bot has multiple nicks\n" +" registered. Also, be sure to configure the NickServ and ChanServ\n" +" configuration variables to match the NickServ and ChanServ nicks on your\n" +" network. Other commands such as identify, op, etc. should not be\n" +" necessary if the bot is properly configured." +msgstr "" + +#: plugin.py:396 +msgid "You must set supybot.plugins.Services.ChanServ before I'm able to send the %s command." +msgstr "" + +#: plugin.py:402 +#, docstring +msgid "" +"[]\n" +"\n" +" Attempts to get opped by ChanServ in . is only\n" +" necessary if the message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:408 +msgid "I'm already opped in %s." +msgstr "" + +#: plugin.py:415 +#, docstring +msgid "" +"[]\n" +"\n" +" Attempts to get voiced by ChanServ in . is only\n" +" necessary if the message isn't sent in the channel itself.\n" +" " +msgstr "" + +#: plugin.py:421 +msgid "I'm already voiced in %s." +msgstr "" + +#: plugin.py:438 +#, docstring +msgid "" +"[]\n" +"\n" +" Attempts to get unbanned by ChanServ in . is only\n" +" necessary if the message isn't sent in the channel itself, but chances\n" +" are, if you need this command, you're not sending it in the channel\n" +" itself.\n" +" " +msgstr "" + +#: plugin.py:459 +#, docstring +msgid "" +"[]\n" +"\n" +" Attempts to get invited by ChanServ to . is only\n" +" necessary if the message isn't sent in the channel itself, but chances\n" +" are, if you need this command, you're not sending it in the channel\n" +" itself.\n" +" " +msgstr "" + +#: plugin.py:480 +#, docstring +msgid "" +"takes no arguments\n" +"\n" +" Identifies with NickServ using the current nick.\n" +" " +msgstr "" + +#: plugin.py:489 +msgid "I don't have a configured password for my current nick." +msgstr "" + +#: plugin.py:492 +msgid "You must set supybot.plugins.Services.NickServ before I'm able to do identify." +msgstr "" + +#: plugin.py:498 +#, docstring +msgid "" +"[]\n" +"\n" +" Ghosts the bot's given nick and takes it. If no nick is given,\n" +" ghosts the bot's configured nick and takes it.\n" +" " +msgstr "" + +#: plugin.py:507 +msgid "I cowardly refuse to ghost myself." +msgstr "" + +#: plugin.py:512 +msgid "You must set supybot.plugins.Services.NickServ before I'm able to ghost a nick." +msgstr "" + +#: plugin.py:518 +#, docstring +msgid "" +" []\n" +"\n" +" Sets the NickServ password for to . If is\n" +" not given, removes from the configured nicks.\n" +" " +msgstr "" + +#: plugin.py:528 +msgid "That nick was not configured with a password." +msgstr "" + +#: plugin.py:539 +#, docstring +msgid "" +"takes no arguments\n" +"\n" +" Returns the nicks that this plugin is configured to identify and ghost\n" +" with.\n" +" " +msgstr "" + +#: plugin.py:549 +msgid "I'm not currently configured for any nicks." +msgstr "" + diff --git a/plugins/Services/plugin.py b/plugins/Services/plugin.py index e5882573b..d57ab0966 100644 --- a/plugins/Services/plugin.py +++ b/plugins/Services/plugin.py @@ -40,7 +40,10 @@ import supybot.ircmsgs as ircmsgs import supybot.ircutils as ircutils import supybot.schedule as schedule import supybot.callbacks as callbacks +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Services') +@internationalizeDocstring class Services(callbacks.Plugin): """This plugin handles dealing with Services on networks that provide them. Basically, you should use the "password" command to tell the bot a nick to @@ -390,10 +393,11 @@ class Services(callbacks.Plugin): 'supybot.plugins.Services.ChanServ before ' 'I can send commands to ChanServ.', command) else: - irc.error('You must set supybot.plugins.Services.ChanServ ' - 'before I\'m able to send the %s command.' % command, + irc.error(_('You must set supybot.plugins.Services.ChanServ ' + 'before I\'m able to send the %s command.') % command, Raise=True) + @internationalizeDocstring def op(self, irc, msg, args, channel): """[] @@ -401,11 +405,12 @@ class Services(callbacks.Plugin): necessary if the message isn't sent in the channel itself. """ if irc.nick in irc.state.channels[channel].ops: - irc.error(format('I\'m already opped in %s.', channel)) + irc.error(format(_('I\'m already opped in %s.'), channel)) else: self._chanservCommand(irc, channel, 'op') op = wrap(op, [('checkChannelCapability', 'op'), 'inChannel']) + @internationalizeDocstring def voice(self, irc, msg, args, channel): """[] @@ -413,7 +418,7 @@ class Services(callbacks.Plugin): necessary if the message isn't sent in the channel itself. """ if irc.nick in irc.state.channels[channel].voices: - irc.error(format('I\'m already voiced in %s.', channel)) + irc.error(format(_('I\'m already voiced in %s.'), channel)) else: self._chanservCommand(irc, channel, 'voice') voice = wrap(voice, [('checkChannelCapability', 'op'), 'inChannel']) @@ -428,6 +433,7 @@ class Services(callbacks.Plugin): self._chanservCommand(irc, channel, 'unban', log=True) # Success log in doChanservNotice. + @internationalizeDocstring def unban(self, irc, msg, args, channel): """[] @@ -448,6 +454,7 @@ class Services(callbacks.Plugin): self.log.info('%s is +i, attempting ChanServ invite %s.', channel, on) self._chanservCommand(irc, channel, 'invite', log=True) + @internationalizeDocstring def invite(self, irc, msg, args, channel): """[] @@ -468,6 +475,7 @@ class Services(callbacks.Plugin): self.log.info('Joining %s, invited by ChanServ %s.', channel, on) irc.queueMsg(networkGroup.channels.join(channel)) + @internationalizeDocstring def identify(self, irc, msg, args): """takes no arguments @@ -478,13 +486,14 @@ class Services(callbacks.Plugin): self._doIdentify(irc, irc.nick) irc.replySuccess() else: - irc.error('I don\'t have a configured password for ' - 'my current nick.') + irc.error(_('I don\'t have a configured password for ' + 'my current nick.')) else: - irc.error('You must set supybot.plugins.Services.NickServ before ' - 'I\'m able to do identify.') + irc.error(_('You must set supybot.plugins.Services.NickServ before ' + 'I\'m able to do identify.')) identify = wrap(identify, [('checkCapability', 'admin')]) + @internationalizeDocstring def ghost(self, irc, msg, args, nick): """[] @@ -495,15 +504,16 @@ class Services(callbacks.Plugin): if not nick: nick = self._getNick() if ircutils.strEqual(nick, irc.nick): - irc.error('I cowardly refuse to ghost myself.') + irc.error(_('I cowardly refuse to ghost myself.')) else: self._doGhost(irc, nick=nick) irc.replySuccess() else: - irc.error('You must set supybot.plugins.Services.NickServ before ' - 'I\'m able to ghost a nick.') + irc.error(_('You must set supybot.plugins.Services.NickServ before ' + 'I\'m able to ghost a nick.')) ghost = wrap(ghost, [('checkCapability', 'admin'), additional('nick')]) + @internationalizeDocstring def password(self, irc, msg, args, nick, password): """ [] @@ -515,7 +525,7 @@ class Services(callbacks.Plugin): self.registryValue('nicks').remove(nick) irc.replySuccess() except KeyError: - irc.error('That nick was not configured with a password.') + irc.error(_('That nick was not configured with a password.')) return else: self.registryValue('nicks').add(nick) @@ -524,6 +534,7 @@ class Services(callbacks.Plugin): password = wrap(password, [('checkCapability', 'admin'), 'private', 'nick', 'text']) + @internationalizeDocstring def nicks(self, irc, msg, args): """takes no arguments @@ -535,7 +546,7 @@ class Services(callbacks.Plugin): utils.sortBy(ircutils.toLower, L) irc.reply(format('%L', L)) else: - irc.reply('I\'m not currently configured for any nicks.') + irc.reply(_('I\'m not currently configured for any nicks.')) nicks = wrap(nicks, [('checkCapability', 'admin')]) diff --git a/plugins/ShrinkUrl/config.py b/plugins/ShrinkUrl/config.py index fa5429510..5a2b6355e 100644 --- a/plugins/ShrinkUrl/config.py +++ b/plugins/ShrinkUrl/config.py @@ -30,13 +30,15 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('ShrinkUrl') def configure(advanced): from supybot.questions import output, expect, anything, something, yn conf.registerPlugin('ShrinkUrl', True) - if yn("""This plugin offers a snarfer that will go retrieve a shorter + if yn(_("""This plugin offers a snarfer that will go retrieve a shorter version of long URLs that are sent to the channel. Would you - like this snarfer to be enabled?""", default=False): + like this snarfer to be enabled?"""), default=False): conf.supybot.plugins.ShrinkUrl.shrinkSnarfer.setValue(True) class ShrinkService(registry.OnlySomeStrings): @@ -66,34 +68,34 @@ class ShrinkCycle(registry.SpaceSeparatedListOfStrings): ShrinkUrl = conf.registerPlugin('ShrinkUrl') conf.registerChannelValue(ShrinkUrl, 'shrinkSnarfer', - registry.Boolean(False, """Determines whether the + registry.Boolean(False, _("""Determines whether the shrink snarfer is enabled. This snarfer will watch for URLs in the channel, and if they're sufficiently long (as determined by supybot.plugins.ShrinkUrl.minimumLength) it will post a smaller URL from either ln-s.net or tinyurl.com, as denoted in - supybot.plugins.ShrinkUrl.default.""")) + supybot.plugins.ShrinkUrl.default."""))) conf.registerChannelValue(ShrinkUrl.shrinkSnarfer, 'showDomain', - registry.Boolean(True, """Determines whether the snarfer will show the - domain of the URL being snarfed along with the shrunken URL.""")) + registry.Boolean(True, _("""Determines whether the snarfer will show the + domain of the URL being snarfed along with the shrunken URL."""))) conf.registerChannelValue(ShrinkUrl, 'minimumLength', - registry.PositiveInteger(48, """The minimum length a URL must be before - the bot will shrink it.""")) + registry.PositiveInteger(48, _("""The minimum length a URL must be before + the bot will shrink it."""))) conf.registerChannelValue(ShrinkUrl, 'nonSnarfingRegexp', - registry.Regexp(None, """Determines what URLs are to be snarfed; URLs + registry.Regexp(None, _("""Determines what URLs are to be snarfed; URLs matching the regexp given will not be snarfed. Give the empty string if - you have no URLs that you'd like to exclude from being snarfed.""")) + you have no URLs that you'd like to exclude from being snarfed."""))) conf.registerChannelValue(ShrinkUrl, 'outFilter', - registry.Boolean(False, """Determines whether the bot will shrink the URLs - of outgoing messages if those URLs are longer than - supybot.plugins.ShrinkUrl.minimumLength.""")) + registry.Boolean(False, _("""Determines whether the bot will shrink the + URLs of outgoing messages if those URLs are longer than + supybot.plugins.ShrinkUrl.minimumLength."""))) conf.registerChannelValue(ShrinkUrl, 'default', - ShrinkService('ln', """Determines what website the bot will use when - shrinking a URL.""")) + ShrinkService('ln', _("""Determines what website the bot will use when + shrinking a URL."""))) conf.registerGlobalValue(ShrinkUrl, 'bold', - registry.Boolean(True, """Determines whether this plugin will bold certain - portions of its replies.""")) + registry.Boolean(True, _("""Determines whether this plugin will bold + certain portions of its replies."""))) conf.registerChannelValue(ShrinkUrl, 'serviceRotation', - ShrinkCycle([], """If set to a non-empty value, specifies the list of - services to rotate through for the shrinkSnarfer and outFilter.""")) + ShrinkCycle([], _("""If set to a non-empty value, specifies the list of + services to rotate through for the shrinkSnarfer and outFilter."""))) # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/ShrinkUrl/messages.pot b/plugins/ShrinkUrl/messages.pot new file mode 100644 index 000000000..433b2cd45 --- /dev/null +++ b/plugins/ShrinkUrl/messages.pot @@ -0,0 +1,119 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-20 08:55+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: config.py:39 +msgid "" +"This plugin offers a snarfer that will go retrieve a shorter\n" +" version of long URLs that are sent to the channel. Would you\n" +" like this snarfer to be enabled?" +msgstr "" + +#: config.py:45 config.py:49 +#, docstring +msgid "Valid values include 'ln', 'tiny', 'xrl', and 'x0'." +msgstr "" + +#: config.py:71 +msgid "" +"Determines whether the\n" +" shrink snarfer is enabled. This snarfer will watch for URLs in the\n" +" channel, and if they're sufficiently long (as determined by\n" +" supybot.plugins.ShrinkUrl.minimumLength) it will post a\n" +" smaller URL from either ln-s.net or tinyurl.com, as denoted in\n" +" supybot.plugins.ShrinkUrl.default." +msgstr "" + +#: config.py:78 +msgid "" +"Determines whether the snarfer will show the\n" +" domain of the URL being snarfed along with the shrunken URL." +msgstr "" + +#: config.py:81 +msgid "" +"The minimum length a URL must be before\n" +" the bot will shrink it." +msgstr "" + +#: config.py:84 +msgid "" +"Determines what URLs are to be snarfed; URLs\n" +" matching the regexp given will not be snarfed. Give the empty string if\n" +" you have no URLs that you'd like to exclude from being snarfed." +msgstr "" + +#: config.py:88 +msgid "" +"Determines whether the bot will shrink the\n" +" URLs of outgoing messages if those URLs are longer than\n" +" supybot.plugins.ShrinkUrl.minimumLength." +msgstr "" + +#: config.py:92 +msgid "" +"Determines what website the bot will use when\n" +" shrinking a URL." +msgstr "" + +#: config.py:95 +msgid "" +"Determines whether this plugin will bold\n" +" certain portions of its replies." +msgstr "" + +#: config.py:98 +msgid "" +"If set to a non-empty value, specifies the list of\n" +" services to rotate through for the shrinkSnarfer and outFilter." +msgstr "" + +#: plugin.py:169 +#, docstring +msgid "" +"\n" +"\n" +" Returns an ln-s.net version of .\n" +" " +msgstr "" + +#: plugin.py:194 +#, docstring +msgid "" +"\n" +"\n" +" Returns a TinyURL.com version of \n" +" " +msgstr "" + +#: plugin.py:222 +#, docstring +msgid "" +"\n" +"\n" +" Returns an xrl.us version of .\n" +" " +msgstr "" + +#: plugin.py:248 +#, docstring +msgid "" +"\n" +"\n" +" Returns an x0.no version of .\n" +" " +msgstr "" + diff --git a/plugins/ShrinkUrl/plugin.py b/plugins/ShrinkUrl/plugin.py index d162c27dc..471400186 100644 --- a/plugins/ShrinkUrl/plugin.py +++ b/plugins/ShrinkUrl/plugin.py @@ -37,6 +37,8 @@ import supybot.ircmsgs as ircmsgs import supybot.plugins as plugins import supybot.ircutils as ircutils import supybot.callbacks as callbacks +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('ShrinkUrl') class CdbShrunkenUrlDB(object): def __init__(self, filename): @@ -162,6 +164,7 @@ class ShrinkUrl(callbacks.PluginRegexp): else: raise ShrinkError, text + @internationalizeDocstring def ln(self, irc, msg, args, url): """ @@ -186,6 +189,7 @@ class ShrinkUrl(callbacks.PluginRegexp): self.db.set('tiny', url, text) return text + @internationalizeDocstring def tiny(self, irc, msg, args, url): """ @@ -213,6 +217,7 @@ class ShrinkUrl(callbacks.PluginRegexp): self.db.set('xrl', quotedurl, text) return text + @internationalizeDocstring def xrl(self, irc, msg, args, url): """ @@ -238,6 +243,7 @@ class ShrinkUrl(callbacks.PluginRegexp): self.db.set('x0', url, text) return text + @internationalizeDocstring def x0(self, irc, msg, args, url): """ diff --git a/plugins/Status/config.py b/plugins/Status/config.py index 74d059b45..56cda1717 100644 --- a/plugins/Status/config.py +++ b/plugins/Status/config.py @@ -29,6 +29,8 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Status') def configure(advanced): # This will be called by supybot to configure this module. advanced is @@ -41,13 +43,13 @@ def configure(advanced): Status = conf.registerPlugin('Status') conf.registerGroup(Status, 'cpu') conf.registerChannelValue(Status.cpu, 'children', - registry.Boolean(True, """Determines whether the cpu command will list the - time taken by children as well as the bot's process.""")) + registry.Boolean(True, _("""Determines whether the cpu command will list + the time taken by children as well as the bot's process."""))) conf.registerChannelValue(Status.cpu, 'threads', - registry.Boolean(False, """Determines whether the cpu command will provide - the number of threads spawned and active.""")) + registry.Boolean(False, _("""Determines whether the cpu command will + provide the number of threads spawned and active."""))) conf.registerChannelValue(Status.cpu, 'memory', - registry.Boolean(True, """Determines whether the cpu command will report - the amount of memory being used by the bot.""")) + registry.Boolean(True, _("""Determines whether the cpu command will report + the amount of memory being used by the bot."""))) # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/Status/messages.pot b/plugins/Status/messages.pot new file mode 100644 index 000000000..c186802a1 --- /dev/null +++ b/plugins/Status/messages.pot @@ -0,0 +1,155 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-20 09:05+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: config.py:46 +msgid "" +"Determines whether the cpu command will list\n" +" the time taken by children as well as the bot's process." +msgstr "" + +#: config.py:49 +msgid "" +"Determines whether the cpu command will\n" +" provide the number of threads spawned and active." +msgstr "" + +#: config.py:52 +msgid "" +"Determines whether the cpu command will report\n" +" the amount of memory being used by the bot." +msgstr "" + +#: plugin.py:71 +#, docstring +msgid "" +"takes no arguments\n" +"\n" +" Returns the status of the bot.\n" +" " +msgstr "" + +#: plugin.py:80 +msgid "%s as %L" +msgstr "" + +#: plugin.py:81 +msgid "I am connected to %L." +msgstr "" + +#: plugin.py:83 +msgid "I am currently in code profiling mode." +msgstr "" + +#: plugin.py:89 +#, docstring +msgid "" +"takes no arguments\n" +"\n" +" Returns the current threads that are active.\n" +" " +msgstr "" + +#: plugin.py:95 +msgid "I have spawned %n; %n %b still currently active: %L." +msgstr "" + +#: plugin.py:103 +#, docstring +msgid "" +"takes no arguments\n" +"\n" +" Returns some interesting network-related statistics.\n" +" " +msgstr "" + +#: plugin.py:111 +msgid "an indeterminate amount of time" +msgstr "" + +#: plugin.py:112 +msgid "I have received %s messages for a total of %s bytes. I have sent %s messages for a total of %s bytes. I have been connected to %s for %s." +msgstr "" + +#: plugin.py:121 +#, docstring +msgid "" +"takes no arguments\n" +"\n" +" Returns some interesting CPU-related statistics on the bot.\n" +" " +msgstr "" + +#: plugin.py:131 +msgid "My children have taken %.2f seconds of user time and %.2f seconds of system time for a total of %.2f seconds of CPU time. " +msgstr "" + +#: 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 "" + +#: plugin.py:160 +msgid "Unable to run ps command." +msgstr "" + +#: plugin.py:166 +msgid " I'm taking up %s kB of memory." +msgstr "" + +#: plugin.py:174 +#, docstring +msgid "" +"takes no arguments\n" +"\n" +" Returns some interesting command-related statistics.\n" +" " +msgstr "" + +#: plugin.py:184 +msgid "I offer a total of %n in %n. I have processed %n." +msgstr "" + +#: plugin.py:193 +#, docstring +msgid "" +"takes no arguments\n" +"\n" +" Returns a list of the commands offered by the bot.\n" +" " +msgstr "" + +#: plugin.py:207 +#, docstring +msgid "" +"takes no arguments\n" +"\n" +" Returns the amount of time the bot has been running.\n" +" " +msgstr "" + +#: plugin.py:211 +msgid "I have been running for %s." +msgstr "" + +#: plugin.py:218 +#, docstring +msgid "" +"takes no arguments\n" +"\n" +" Returns the server the bot is on.\n" +" " +msgstr "" + diff --git a/plugins/Status/plugin.py b/plugins/Status/plugin.py index fccd58336..58bda4290 100644 --- a/plugins/Status/plugin.py +++ b/plugins/Status/plugin.py @@ -39,6 +39,8 @@ import supybot.utils as utils import supybot.world as world from supybot.commands import * import supybot.callbacks as callbacks +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Status') class Status(callbacks.Plugin): def __init__(self, irc): @@ -64,6 +66,7 @@ class Status(callbacks.Plugin): def do001(self, irc, msg): self.connected[irc] = time.time() + @internationalizeDocstring def status(self, irc, msg, args): """takes no arguments @@ -74,13 +77,14 @@ class Status(callbacks.Plugin): networks.setdefault(Irc.network, []).append(Irc.nick) networks = networks.items() networks.sort() - networks = [format('%s as %L', net, nicks) for (net,nicks) in networks] - L = [format('I am connected to %L.', networks)] + networks = [format(_('%s as %L'), net, nicks) for (net,nicks) in networks] + L = [format(_('I am connected to %L.'), networks)] if world.profiling: - L.append('I am currently in code profiling mode.') + L.append(_('I am currently in code profiling mode.')) irc.reply(' '.join(L)) status = wrap(status) + @internationalizeDocstring def threads(self, irc, msg, args): """takes no arguments @@ -88,12 +92,13 @@ class Status(callbacks.Plugin): """ threads = [t.getName() for t in threading.enumerate()] threads.sort() - s = format('I have spawned %n; %n %b still currently active: %L.', + s = format(_('I have spawned %n; %n %b still currently active: %L.'), (world.threadsSpawned, 'thread'), (len(threads), 'thread'), len(threads), threads) irc.reply(s) threads = wrap(threads) + @internationalizeDocstring def net(self, irc, msg, args): """takes no arguments @@ -103,14 +108,15 @@ class Status(callbacks.Plugin): elapsed = time.time() - self.connected[irc.getRealIrc()] timeElapsed = utils.timeElapsed(elapsed) except KeyError: - timeElapsed = 'an indeterminate amount of time' - irc.reply('I have received %s messages for a total of %s bytes. ' + timeElapsed = _('an indeterminate amount of time') + irc.reply(_('I have received %s messages for a total of %s bytes. ' 'I have sent %s messages for a total of %s bytes. ' - 'I have been connected to %s for %s.' % + 'I have been connected to %s for %s.') % (self.recvdMsgs, self.recvdBytes, self.sentMsgs, self.sentBytes, irc.server, timeElapsed)) net = wrap(net) + @internationalizeDocstring def cpu(self, irc, msg, args): """takes no arguments @@ -122,16 +128,16 @@ class Status(callbacks.Plugin): timeRunning = now - world.startedAt if self.registryValue('cpu.children', target) and \ user+system < timeRunning+1: # Fudge for FPU inaccuracies. - children = 'My children have taken %.2f seconds of user time ' \ - 'and %.2f seconds of system time ' \ - 'for a total of %.2f seconds of CPU time. ' % \ + children = _('My children have taken %.2f seconds of user time ' + 'and %.2f seconds of system time ' + 'for a total of %.2f seconds of CPU time. ') % \ (childUser, childSystem, childUser+childSystem) else: children = '' activeThreads = threading.activeCount() - response = 'I have taken %.2f seconds of user time and %.2f seconds ' \ - 'of system time, for a total of %.2f seconds of CPU ' \ - 'time. %s' % (user, system, user + system, children) + response = _('I have taken %.2f seconds of user time and %.2f seconds ' + 'of system time, for a total of %.2f seconds of CPU ' + 'time. %s') % (user, system, user + system, children) if self.registryValue('cpu.threads', target): response += format('I have spawned %n; I currently have %i still ' 'running.', @@ -151,18 +157,19 @@ class Status(callbacks.Plugin): stdout=subprocess.PIPE, stderr=subprocess.PIPE) except OSError: - irc.error('Unable to run ps command.', Raise=True) - (out, _) = inst.communicate() + irc.error(_('Unable to run ps command.'), Raise=True) + (out, foo) = inst.communicate() inst.wait() mem = out.splitlines()[1] elif sys.platform.startswith('netbsd'): mem = '%s kB' % os.stat('/proc/%s/mem' % pid)[7] - response += ' I\'m taking up %s kB of memory.' % mem + response += _(' I\'m taking up %s kB of memory.') % mem except Exception: self.log.exception('Uncaught exception in cpu.memory:') irc.reply(utils.str.normalizeWhitespace(response)) cpu = wrap(cpu) + @internationalizeDocstring def cmd(self, irc, msg, args): """takes no arguments @@ -174,13 +181,14 @@ class Status(callbacks.Plugin): if isinstance(cb, callbacks.Plugin): callbacksPlugin += 1 commands += len(cb.listCommands()) - s = format('I offer a total of %n in %n. I have processed %n.', + s = format(_('I offer a total of %n in %n. I have processed %n.'), (commands, 'command'), (callbacksPlugin, 'command-based', 'plugin'), (world.commandsProcessed, 'command')) irc.reply(s) cmd = wrap(cmd) + @internationalizeDocstring def commands(self, irc, msg, args): """takes no arguments @@ -194,16 +202,18 @@ class Status(callbacks.Plugin): irc.reply(format('%L', sorted(commands))) commands = wrap(commands) + @internationalizeDocstring def uptime(self, irc, msg, args): """takes no arguments Returns the amount of time the bot has been running. """ - response = 'I have been running for %s.' % \ + response = _('I have been running for %s.') % \ utils.timeElapsed(time.time() - world.startedAt) irc.reply(response) uptime = wrap(uptime) + @internationalizeDocstring def server(self, irc, msg, args): """takes no arguments diff --git a/plugins/String/config.py b/plugins/String/config.py index e4f1840ad..d451b6a9e 100644 --- a/plugins/String/config.py +++ b/plugins/String/config.py @@ -29,6 +29,8 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('String') def configure(advanced): # This will be called by supybot to configure this module. advanced is @@ -42,13 +44,13 @@ def configure(advanced): String = conf.registerPlugin('String') conf.registerGroup(String, 'levenshtein') conf.registerGlobalValue(String.levenshtein, 'max', - registry.PositiveInteger(256, """Determines the maximum size of a string + registry.PositiveInteger(256, _("""Determines the maximum size of a string given to the levenshtein command. The levenshtein command uses an O(m*n) algorithm, which means that with strings of length 256, it can take 1.5 seconds to finish; with strings of length 384, though, it can take 4 seconds to finish, and with strings of much larger lengths, it takes more and more time. Using nested commands, strings can get quite large, hence this variable, to limit the size of arguments passed to the levenshtein - command.""")) + command."""))) # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/String/messages.pot b/plugins/String/messages.pot new file mode 100644 index 000000000..1d593ab4b --- /dev/null +++ b/plugins/String/messages.pot @@ -0,0 +1,168 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-20 09:08+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: config.py:47 +msgid "" +"Determines the maximum size of a string\n" +" given to the levenshtein command. The levenshtein command uses an O(m*n)\n" +" algorithm, which means that with strings of length 256, it can take 1.5\n" +" seconds to finish; with strings of length 384, though, it can take 4\n" +" seconds to finish, and with strings of much larger lengths, it takes more\n" +" and more time. Using nested commands, strings can get quite large, hence\n" +" this variable, to limit the size of arguments passed to the levenshtein\n" +" command." +msgstr "" + +#: plugin.py:46 +#, docstring +msgid "" +"\n" +"\n" +" Returns the 8-bit value of .\n" +" " +msgstr "" + +#: plugin.py:55 +#, docstring +msgid "" +"\n" +"\n" +" Returns the character associated with the 8-bit value \n" +" " +msgstr "" + +#: plugin.py:62 +msgid "That number doesn't map to an 8-bit character." +msgstr "" + +#: plugin.py:67 +#, docstring +msgid "" +" \n" +"\n" +" Returns an encoded form of the given text; the valid encodings are\n" +" available in the documentation of the Python codecs module:\n" +" .\n" +" " +msgstr "" + +#: plugin.py:76 plugin.py:90 +msgid "encoding" +msgstr "" + +#: plugin.py:81 +#, docstring +msgid "" +" \n" +"\n" +" Returns an un-encoded form of the given text; the valid encodings are\n" +" available in the documentation of the Python codecs module:\n" +" .\n" +" " +msgstr "" + +#: plugin.py:92 +msgid "base64 string" +msgstr "" + +#: plugin.py:93 +msgid "Base64 strings must be a multiple of 4 in length, padded with '=' if necessary." +msgstr "" + +#: plugin.py:99 +#, docstring +msgid "" +" \n" +"\n" +" Returns the levenshtein distance (also known as the \"edit distance\"\n" +" between and )\n" +" " +msgstr "" + +#: plugin.py:106 +msgid "Levenshtein distance is a complicated algorithm, try it with some smaller inputs." +msgstr "" + +#: plugin.py:114 +#, docstring +msgid "" +" []\n" +"\n" +" Returns the Soundex hash to a given length. The length defaults to\n" +" 4, since that's the standard length for a soundex hash. For unlimited\n" +" length, use 0.\n" +" " +msgstr "" + +#: plugin.py:125 +#, docstring +msgid "" +"\n" +"\n" +" Returns the length of .\n" +" " +msgstr "" + +#: plugin.py:134 +#, docstring +msgid "" +" \n" +"\n" +" If is of the form m/regexp/flags, returns the portion of\n" +" that matches the regexp. If is of the form\n" +" s/regexp/replacement/flags, returns the result of applying such a\n" +" regexp to .\n" +" " +msgstr "" + +#: plugin.py:146 +msgid "You probably don't want to match the empty string." +msgstr "" + +#: plugin.py:156 +#, docstring +msgid "" +" \n" +"\n" +" Returns XOR-encrypted with . See\n" +" http://www.yoe.org/developer/xor.html for information about XOR\n" +" encryption.\n" +" " +msgstr "" + +#: plugin.py:169 +#, docstring +msgid "" +"\n" +"\n" +" Returns the md5 hash of a given string. Read\n" +" http://www.rsasecurity.com/rsalabs/faq/3-6-6.html for more information\n" +" about md5.\n" +" " +msgstr "" + +#: plugin.py:180 +#, docstring +msgid "" +"\n" +"\n" +" Returns the SHA hash of a given string. Read\n" +" http://www.secure-hash-algorithm-md5-sha-1.co.uk/ for more information\n" +" about SHA.\n" +" " +msgstr "" + diff --git a/plugins/String/plugin.py b/plugins/String/plugin.py index d6b4c0dbe..6d0073529 100644 --- a/plugins/String/plugin.py +++ b/plugins/String/plugin.py @@ -36,9 +36,12 @@ from supybot.commands import * import supybot.plugins as plugins import supybot.ircutils as ircutils import supybot.callbacks as callbacks +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('String') class String(callbacks.Plugin): + @internationalizeDocstring def ord(self, irc, msg, args, letter): """ @@ -47,6 +50,7 @@ class String(callbacks.Plugin): irc.reply(str(ord(letter))) ord = wrap(ord, ['letter']) + @internationalizeDocstring def chr(self, irc, msg, args, i): """ @@ -55,9 +59,10 @@ class String(callbacks.Plugin): try: irc.reply(chr(i)) except ValueError: - irc.error('That number doesn\'t map to an 8-bit character.') + irc.error(_('That number doesn\'t map to an 8-bit character.')) chr = wrap(chr, ['int']) + @internationalizeDocstring def encode(self, irc, msg, args, encoding, text): """ @@ -68,9 +73,10 @@ class String(callbacks.Plugin): try: irc.reply(text.encode(encoding).rstrip('\n')) except LookupError: - irc.errorInvalid('encoding', encoding) + irc.errorInvalid(_('encoding'), encoding) encode = wrap(encode, ['something', 'text']) + @internationalizeDocstring def decode(self, irc, msg, args, encoding, text): """ @@ -81,13 +87,14 @@ class String(callbacks.Plugin): try: irc.reply(text.decode(encoding).encode('utf-8')) except LookupError: - irc.errorInvalid('encoding', encoding) + irc.errorInvalid(_('encoding'), encoding) except binascii.Error: - irc.errorInvalid('base64 string', - s='Base64 strings must be a multiple of 4 in ' - 'length, padded with \'=\' if necessary.') + irc.errorInvalid(_('base64 string'), + s=_('Base64 strings must be a multiple of 4 in ' + 'length, padded with \'=\' if necessary.')) decode = wrap(decode, ['something', 'text']) + @internationalizeDocstring def levenshtein(self, irc, msg, args, s1, s2): """ @@ -96,12 +103,13 @@ class String(callbacks.Plugin): """ max = self.registryValue('levenshtein.max') if len(s1) > max or len(s2) > max: - irc.error('Levenshtein distance is a complicated algorithm, try ' - 'it with some smaller inputs.') + irc.error(_('Levenshtein distance is a complicated algorithm, try ' + 'it with some smaller inputs.')) else: irc.reply(str(utils.str.distance(s1, s2))) levenshtein = wrap(levenshtein, ['something', 'text']) + @internationalizeDocstring def soundex(self, irc, msg, args, text, length): """ [] @@ -112,6 +120,7 @@ class String(callbacks.Plugin): irc.reply(utils.str.soundex(text, length)) soundex = wrap(soundex, ['somethingWithoutSpaces', additional('int', 4)]) + @internationalizeDocstring def len(self, irc, msg, args, text): """ @@ -120,6 +129,7 @@ class String(callbacks.Plugin): irc.reply(str(len(text))) len = wrap(len, ['text']) + @internationalizeDocstring def re(self, irc, msg, args, ff, text): """ @@ -133,7 +143,7 @@ class String(callbacks.Plugin): else: f = lambda s: ff.search(s) and ff.search(s).group(0) or '' if f('') and len(f(' ')) > len(f(''))+1: # Matches the empty string. - s = 'You probably don\'t want to match the empty string.' + s = _('You probably don\'t want to match the empty string.') irc.error(s) else: irc.reply(f(text)) @@ -141,6 +151,7 @@ class String(callbacks.Plugin): first('regexpMatcher', 'regexpReplacer'), 'text']) + @internationalizeDocstring def xor(self, irc, msg, args, password, text): """ @@ -153,6 +164,7 @@ class String(callbacks.Plugin): irc.reply(''.join(ret)) xor = wrap(xor, ['something', 'text']) + @internationalizeDocstring def md5(self, irc, msg, args, text): """ @@ -163,6 +175,7 @@ class String(callbacks.Plugin): irc.reply(utils.crypt.md5(text).hexdigest()) md5 = wrap(md5, ['text']) + @internationalizeDocstring def sha(self, irc, msg, args, text): """ diff --git a/plugins/Success/config.py b/plugins/Success/config.py index 8a0cabb4f..72ddf49e3 100644 --- a/plugins/Success/config.py +++ b/plugins/Success/config.py @@ -29,6 +29,8 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Success') def configure(advanced): # This will be called by supybot to configure this module. advanced is @@ -42,10 +44,10 @@ def configure(advanced): Success = conf.registerPlugin('Success') # This is where your configuration variables (if any) should go. For example: # conf.registerGlobalValue(Success, 'someConfigVariableName', -# registry.Boolean(False, """Help for someConfigVariableName.""")) +# registry.Boolean(False, _("""Help for someConfigVariableName."""))) conf.registerChannelValue(conf.supybot.plugins.Success, 'prefixNick', - registry.Boolean(True, """Determines whether the bot will prefix the nick - of the user giving an invalid command to the success response.""")) + registry.Boolean(True, _("""Determines whether the bot will prefix the nick + of the user giving an invalid command to the success response."""))) # vim:set shiftwidth=4 softtabstop=8 expandtab textwidth=78 diff --git a/plugins/Success/messages.pot b/plugins/Success/messages.pot new file mode 100644 index 000000000..361535e0e --- /dev/null +++ b/plugins/Success/messages.pot @@ -0,0 +1,33 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-20 09:09+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: config.py:49 +msgid "" +"Determines whether the bot will prefix the nick\n" +" of the user giving an invalid command to the success response." +msgstr "" + +#: plugin.py:39 +#, docstring +msgid "" +"This plugin was written initially to work with MoobotFactoids, the two\n" +" of them to provide a similar-to-moobot-and-blootbot interface for factoids.\n" +" Basically, it replaces the standard 'The operation succeeded.' messages\n" +" with messages kept in a database, able to give more personable\n" +" responses." +msgstr "" + diff --git a/plugins/Success/plugin.py b/plugins/Success/plugin.py index ec6adbcf0..572b1e340 100644 --- a/plugins/Success/plugin.py +++ b/plugins/Success/plugin.py @@ -31,7 +31,10 @@ import supybot.conf as conf from supybot.commands import * import supybot.plugins as plugins import supybot.ircutils as ircutils +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Success') +@internationalizeDocstring class Success(plugins.ChannelIdDatabasePlugin): """This plugin was written initially to work with MoobotFactoids, the two of them to provide a similar-to-moobot-and-blootbot interface for factoids. From 6a9a8d81b81dd0a7e7e0ff1608d25b56e33d9bd0 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Wed, 20 Oct 2010 09:39:44 +0200 Subject: [PATCH 036/136] Internationalize Time, Todo, Topic, URL, Unix, Utilities, and Web --- plugins/Time/config.py | 6 +- plugins/Time/messages.pot | 97 +++++++++ plugins/Time/plugin.py | 14 +- plugins/Todo/config.py | 4 +- plugins/Todo/messages.pot | 129 ++++++++++++ plugins/Todo/plugin.py | 42 ++-- plugins/Topic/config.py | 26 +-- plugins/Topic/messages.pot | 356 +++++++++++++++++++++++++++++++++ plugins/Topic/plugin.py | 67 +++++-- plugins/URL/config.py | 6 +- plugins/URL/messages.pot | 58 ++++++ plugins/URL/plugin.py | 8 +- plugins/Unix/config.py | 36 ++-- plugins/Unix/messages.pot | 198 ++++++++++++++++++ plugins/Unix/plugin.py | 46 +++-- plugins/Utilities/config.py | 4 +- plugins/Utilities/messages.pot | 81 ++++++++ plugins/Utilities/plugin.py | 8 + plugins/Web/config.py | 18 +- plugins/Web/messages.pot | 156 +++++++++++++++ plugins/Web/plugin.py | 36 ++-- 21 files changed, 1279 insertions(+), 117 deletions(-) create mode 100644 plugins/Time/messages.pot create mode 100644 plugins/Todo/messages.pot create mode 100644 plugins/Topic/messages.pot create mode 100644 plugins/URL/messages.pot create mode 100644 plugins/Unix/messages.pot create mode 100644 plugins/Utilities/messages.pot create mode 100644 plugins/Web/messages.pot diff --git a/plugins/Time/config.py b/plugins/Time/config.py index 6317c41cc..cb56cf3b5 100644 --- a/plugins/Time/config.py +++ b/plugins/Time/config.py @@ -29,6 +29,8 @@ import supybot.conf as conf import supybot.registry as registry +from supybot.i18n import PluginInternationalization, internationalizeDocstring +_ = PluginInternationalization('Time') def configure(advanced): # This will be called by supybot to configure this module. advanced is @@ -40,10 +42,10 @@ def configure(advanced): Time = conf.registerPlugin('Time') conf.registerChannelValue(Time, 'format', - registry.String(str(conf.supybot.reply.format.time()), """Determines the + registry.String(str(conf.supybot.reply.format.time()), _("""Determines the format string for timestamps. Refer to the Python documentation for the time module to see what formats are accepted. If you set this variable to - the empty string, the timestamp will not be shown.""")) + the empty string, the timestamp will not be shown."""))) # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/Time/messages.pot b/plugins/Time/messages.pot new file mode 100644 index 000000000..cd16ce41c --- /dev/null +++ b/plugins/Time/messages.pot @@ -0,0 +1,97 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2010-10-20 09:38+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: config.py:45 +msgid "" +"Determines the\n" +" format string for timestamps. Refer to the Python documentation for the\n" +" time module to see what formats are accepted. If you set this variable to\n" +" the empty string, the timestamp will not be shown." +msgstr "" + +#: plugin.py:60 +#, docstring +msgid "" +"[y] [w] [d] [h] [m] [s]\n" +"\n" +" Returns the number of seconds in the number of , ,\n" +" , , , and given. An example usage is\n" +" \"seconds 2h 30m\", which would return 9000, which is '3600*2 + 30*60'.\n" +" Useful for scheduling events at a given number of seconds in the\n" +" future.\n" +" " +msgstr "" + +#: plugin.py:95 +#, docstring +msgid "" +"
  • Bar

    should pop to 'table', not 'p'. -

    Foo

    Bar

    should pop to 'tr', not 'p'. -

    FooBar

    should pop to 'p', not 'b'. - -

    • *
    • * should pop to 'ul', not the first 'li'. -
  • ** should pop to 'table', not the first 'tr' - tag should - implicitly close the previous tag within the same
    ** should pop to 'tr', not the first 'td' - """ - - nestingResetTriggers = self.NESTABLE_TAGS.get(name) - isNestable = nestingResetTriggers != None - isResetNesting = self.RESET_NESTING_TAGS.has_key(name) - popTo = None - inclusive = True - for i in range(len(self.tagStack)-1, 0, -1): - p = self.tagStack[i] - if (not p or p.name == name) and not isNestable: - #Non-nestable tags get popped to the top or to their - #last occurance. - popTo = name - break - if (nestingResetTriggers != None - and p.name in nestingResetTriggers) \ - or (nestingResetTriggers == None and isResetNesting - and self.RESET_NESTING_TAGS.has_key(p.name)): - - #If we encounter one of the nesting reset triggers - #peculiar to this tag, or we encounter another tag - #that causes nesting to reset, pop up to but not - #including that tag. - - popTo = p.name - inclusive = False - break - p = p.parent - if popTo: - self._popToTag(popTo, inclusive) - - def unknown_starttag(self, name, attrs, selfClosing=0): - #print "Start tag %s" % name - if self.quoteStack: - #This is not a real tag. - #print "<%s> is not real!" % name - attrs = ''.join(map(lambda(x, y): ' %s="%s"' % (x, y), attrs)) - self.handle_data('<%s%s>' % (name, attrs)) - return - self.endData() - if not name in self.SELF_CLOSING_TAGS and not selfClosing: - self._smartPop(name) - tag = Tag(name, attrs, self.currentTag, self.previous) - if self.previous: - self.previous.next = tag - self.previous = tag - self.pushTag(tag) - if selfClosing or name in self.SELF_CLOSING_TAGS: - self.popTag() - if name in self.QUOTE_TAGS: - #print "Beginning quote (%s)" % name - self.quoteStack.append(name) - self.literal = 1 - - def unknown_endtag(self, name): - if self.quoteStack and self.quoteStack[-1] != name: - #This is not a real end tag. - #print " is not real!" % name - self.handle_data('' % name) - return - self.endData() - self._popToTag(name) - if self.quoteStack and self.quoteStack[-1] == name: - self.quoteStack.pop() - self.literal = (len(self.quoteStack) > 0) - - def handle_data(self, data): - self.currentData.append(data) - - def handle_pi(self, text): - "Propagate processing instructions right through." - self.handle_data("" % text) - - def handle_comment(self, text): - "Propagate comments right through." - self.handle_data("" % text) - - def handle_charref(self, ref): - "Propagate char refs right through." - self.handle_data('&#%s;' % ref) - - def handle_entityref(self, ref): - "Propagate entity refs right through." - self.handle_data('&%s;' % ref) - - def handle_decl(self, data): - "Propagate DOCTYPEs and the like right through." - self.handle_data('' % data) - - def parse_declaration(self, i): - """Treat a bogus SGML declaration as raw data. Treat a CDATA - declaration as regular data.""" - j = None - if self.rawdata[i:i+9] == '', i) - if k == -1: - k = len(self.rawdata) - self.handle_data(self.rawdata[i+9:k]) - j = k+3 - else: - try: - j = SGMLParser.parse_declaration(self, i) - except SGMLParseError: - toHandle = self.rawdata[i:] - self.handle_data(toHandle) - j = i + len(toHandle) - return j - -class BeautifulSoup(BeautifulStoneSoup): - - """This parser knows the following facts about HTML: - - * Some tags have no closing tag and should be interpreted as being - closed as soon as they are encountered. - - * The text inside some tags (ie. 'script') may contain tags which - are not really part of the document and which should be parsed - as text, not tags. If you want to parse the text as tags, you can - always fetch it and parse it explicitly. - - * Tag nesting rules: - - Most tags can't be nested at all. For instance, the occurance of - a

    tag should implicitly close the previous

    tag. - -

    Para1

    Para2 - should be transformed into: -

    Para1

    Para2 - - Some tags can be nested arbitrarily. For instance, the occurance - of a

    tag should _not_ implicitly close the previous -
    tag. - - Alice said:
    Bob said:
    Blah - should NOT be transformed into: - Alice said:
    Bob said:
    Blah - - Some tags can be nested, but the nesting is reset by the - interposition of other tags. For instance, a
    , - but not close a tag in another table. - -
    BlahBlah - should be transformed into: -
    BlahBlah - but, - Blah
    Blah - should NOT be transformed into - Blah
    Blah - - Differing assumptions about tag nesting rules are a major source - of problems with the BeautifulSoup class. If BeautifulSoup is not - treating as nestable a tag your page author treats as nestable, - try ICantBelieveItsBeautifulSoup before writing your own - subclass.""" - - SELF_CLOSING_TAGS = buildTagMap(None, ['br' , 'hr', 'input', 'img', 'meta', - 'spacer', 'link', 'frame', 'base']) - - QUOTE_TAGS = {'script': None} - - #According to the HTML standard, each of these inline tags can - #contain another tag of the same type. Furthermore, it's common - #to actually use these tags this way. - NESTABLE_INLINE_TAGS = ['span', 'font', 'q', 'object', 'bdo', 'sub', 'sup', - 'center'] - - #According to the HTML standard, these block tags can contain - #another tag of the same type. Furthermore, it's common - #to actually use these tags this way. - NESTABLE_BLOCK_TAGS = ['blockquote', 'div', 'fieldset', 'ins', 'del'] - - #Lists can contain other lists, but there are restrictions. - NESTABLE_LIST_TAGS = { 'ol' : [], - 'ul' : [], - 'li' : ['ul', 'ol'], - 'dl' : [], - 'dd' : ['dl'], - 'dt' : ['dl'] } - - #Tables can contain other tables, but there are restrictions. - NESTABLE_TABLE_TAGS = {'table' : [], - 'tr' : ['table', 'tbody', 'tfoot', 'thead'], - 'td' : ['tr'], - 'th' : ['tr'], - } - - NON_NESTABLE_BLOCK_TAGS = ['address', 'form', 'p', 'pre'] - - #If one of these tags is encountered, all tags up to the next tag of - #this type are popped. - RESET_NESTING_TAGS = buildTagMap(None, NESTABLE_BLOCK_TAGS, 'noscript', - NON_NESTABLE_BLOCK_TAGS, - NESTABLE_LIST_TAGS, - NESTABLE_TABLE_TAGS) - - NESTABLE_TAGS = buildTagMap([], NESTABLE_INLINE_TAGS, NESTABLE_BLOCK_TAGS, - NESTABLE_LIST_TAGS, NESTABLE_TABLE_TAGS) - -class ICantBelieveItsBeautifulSoup(BeautifulSoup): - - """The BeautifulSoup class is oriented towards skipping over - common HTML errors like unclosed tags. However, sometimes it makes - errors of its own. For instance, consider this fragment: - - FooBar - - This is perfectly valid (if bizarre) HTML. However, the - BeautifulSoup class will implicitly close the first b tag when it - encounters the second 'b'. It will think the author wrote - "FooBar", and didn't close the first 'b' tag, because - there's no real-world reason to bold something that's already - bold. When it encounters '' it will close two more 'b' - tags, for a grand total of three tags closed instead of two. This - can throw off the rest of your document structure. The same is - true of a number of other tags, listed below. - - It's much more common for someone to forget to close (eg.) a 'b' - tag than to actually use nested 'b' tags, and the BeautifulSoup - class handles the common case. This class handles the - not-co-common case: where you can't believe someone wrote what - they did, but it's valid HTML and BeautifulSoup screwed up by - assuming it wouldn't be. - - If this doesn't do what you need, try subclassing this class or - BeautifulSoup, and providing your own list of NESTABLE_TAGS.""" - - I_CANT_BELIEVE_THEYRE_NESTABLE_INLINE_TAGS = \ - ['em', 'big', 'i', 'small', 'tt', 'abbr', 'acronym', 'strong', - 'cite', 'code', 'dfn', 'kbd', 'samp', 'strong', 'var', 'b', - 'big'] - - I_CANT_BELIEVE_THEYRE_NESTABLE_BLOCK_TAGS = ['noscript'] - - NESTABLE_TAGS = buildTagMap([], BeautifulSoup.NESTABLE_TAGS, - I_CANT_BELIEVE_THEYRE_NESTABLE_BLOCK_TAGS, - I_CANT_BELIEVE_THEYRE_NESTABLE_INLINE_TAGS) - -class BeautifulSOAP(BeautifulStoneSoup): - """This class will push a tag with only a single string child into - the tag's parent as an attribute. The attribute's name is the tag - name, and the value is the string child. An example should give - the flavor of the change: - - baz - => - baz - - You can then access fooTag['bar'] instead of fooTag.barTag.string. - - This is, of course, useful for scraping structures that tend to - use subelements instead of attributes, such as SOAP messages. Note - that it modifies its input, so don't print the modified version - out. - - I'm not sure how many people really want to use this class; let me - know if you do. Mainly I like the name.""" - - def popTag(self): - if len(self.tagStack) > 1: - tag = self.tagStack[-1] - parent = self.tagStack[-2] - parent._getAttrMap() - if (isinstance(tag, Tag) and len(tag.contents) == 1 and - isinstance(tag.contents[0], NavigableText) and - not parent.attrMap.has_key(tag.name)): - parent[tag.name] = tag.contents[0] - BeautifulStoneSoup.popTag(self) - -#Enterprise class names! It has come to our attention that some people -#think the names of the Beautiful Soup parser classes are too silly -#and "unprofessional" for use in enterprise screen-scraping. We feel -#your pain! For such-minded folk, the Beautiful Soup Consortium And -#All-Night Kosher Bakery recommends renaming this file to -#"RobustParser.py" (or, in cases of extreme enterprisitude, -#"RobustParserBeanInterface.class") and using the following -#enterprise-friendly class aliases: -class RobustXMLParser(BeautifulStoneSoup): - pass -class RobustHTMLParser(BeautifulSoup): - pass -class RobustWackAssHTMLParser(ICantBelieveItsBeautifulSoup): - pass -class SimplifyingSOAPParser(BeautifulSOAP): - pass - -### - - -#By default, act as an HTML pretty-printer. -if __name__ == '__main__': - import sys - soup = BeautifulStoneSoup(sys.stdin.read()) - print soup.prettify() diff --git a/plugins/Debian/__init__.py b/plugins/Debian/__init__.py deleted file mode 100644 index 535f2ff85..000000000 --- a/plugins/Debian/__init__.py +++ /dev/null @@ -1,62 +0,0 @@ -### -# Copyright (c) 2003-2005, James Vega -# 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. -### - -""" -This is a module to contain Debian-specific commands. -""" - -import supybot -import supybot.world as world - -# Use this for the version of this plugin. You may wish to put a CVS keyword -# in here if you're keeping the plugin in CVS or some similar system. -__version__ = "0.1" - -__author__ = supybot.authors.jamessan - -# This is a dictionary mapping supybot.Author instances to lists of -# contributions. -__contributors__ = {} - -import config -import plugin -reload(plugin) # In case we're being reloaded. -# Add more reloads here if you add third-party modules and want them to be -# reloaded when this plugin is reloaded. Don't forget to import them as well! -import BeautifulSoup -reload(BeautifulSoup) - -if world.testing: - import test - -Class = plugin.Class -configure = config.configure - - -# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/Debian/config.py b/plugins/Debian/config.py deleted file mode 100644 index efd8a8c51..000000000 --- a/plugins/Debian/config.py +++ /dev/null @@ -1,75 +0,0 @@ -### -# Copyright (c) 2003-2005, James Vega -# 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. -### - -import supybot.conf as conf -import supybot.registry as registry - -def configure(advanced): - # This will be called by supybot to configure this module. advanced is - # a bool that specifies whether the user identified himself as an advanced - # user or not. You should effect your configuration by manipulating the - # registry as appropriate. - from supybot.questions import output, expect, anything, something, yn - conf.registerPlugin('Debian', True) - if not utils.findBinaryInPath('zgrep'): - if not advanced: - output("""I can't find zgrep in your path. This is necessary - to run the file command. I'll disable this command - now. When you get zgrep in your path, use the command - 'enable Debian.file' to re-enable the command.""") - capabilities = conf.supybot.capabilities() - capabilities.add('-Debian.file') - conf.supybot.capabilities.set(capabilities) - else: - output("""I can't find zgrep in your path. If you want to run - the file command with any sort of expediency, you'll - need it. You can use a python equivalent, but it's - about two orders of magnitude slower. THIS MEANS IT - WILL TAKE AGES TO RUN THIS COMMAND. Don't do this.""") - if yn('Do you want to use a Python equivalent of zgrep?'): - conf.supybot.plugins.Debian.pythonZgrep.setValue(True) - else: - output('I\'ll disable file now.') - capabilities = conf.supybot.capabilities() - capabilities.add('-Debian.file') - conf.supybot.capabilities.set(capabilities) - - -Debian = conf.registerPlugin('Debian') -conf.registerGlobalValue(Debian, 'pythonZgrep', - registry.Boolean(False, """An advanced option, mostly just for testing; - uses a Python-coded zgrep rather than the actual zgrep executable, - generally resulting in a 50x slowdown. What would take 2 seconds will - take 100 with this enabled. Don't enable this.""")) -conf.registerChannelValue(Debian, 'bold', - registry.Boolean(True, """Determines whether the plugin will use bold in - the responses to some of its commands.""")) - - -# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/Debian/plugin.py b/plugins/Debian/plugin.py deleted file mode 100644 index 2bc089ab3..000000000 --- a/plugins/Debian/plugin.py +++ /dev/null @@ -1,492 +0,0 @@ -### -# Copyright (c) 2003-2005, James Vega -# 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. -### - -import os -import re -import gzip -import time -import popen2 -import fnmatch -import threading - -import BeautifulSoup - -import supybot.conf as conf -import supybot.utils as utils -import supybot.world as world -from supybot.commands import * -import supybot.plugins as plugins -import supybot.ircutils as ircutils -import supybot.callbacks as callbacks -from supybot.utils.iter import all, imap, ifilter - -class PeriodicFileDownloader(object): - """A class to periodically download a file/files. - - A class-level dictionary 'periodicFiles' maps names of files to - three-tuples of - (url, seconds between downloads, function to run with downloaded file). - - 'url' should be in some form that urllib2.urlopen can handle (do note that - urllib2.urlopen handles file:// links perfectly well.) - - 'seconds between downloads' is the number of seconds between downloads, - obviously. An important point to remember, however, is that it is only - engaged when a command is run. I.e., if you say you want the file - downloaded every day, but no commands that use it are run in a week, the - next time such a command is run, it'll be using a week-old file. If you - don't want such behavior, you'll have to give an error mess age to the user - and tell him to call you back in the morning. - - 'function to run with downloaded file' is a function that will be passed - a string *filename* of the downloaded file. This will be some random - filename probably generated via some mktemp-type-thing. You can do what - you want with this; you may want to build a database, take some stats, - or simply rename the file. You can pass None as your function and the - file with automatically be renamed to match the filename you have it listed - under. It'll be in conf.supybot.directories.data, of course. - - Aside from that dictionary, simply use self.getFile(filename) in any method - that makes use of a periodically downloaded file, and you'll be set. - """ - periodicFiles = None - def __init__(self, *args, **kwargs): - if self.periodicFiles is None: - raise ValueError, 'You must provide files to download' - self.lastDownloaded = {} - self.downloadedCounter = {} - for filename in self.periodicFiles: - if self.periodicFiles[filename][-1] is None: - fullname = os.path.join(conf.supybot.directories.data(), - filename) - if os.path.exists(fullname): - self.lastDownloaded[filename] = os.stat(fullname).st_ctime - else: - self.lastDownloaded[filename] = 0 - else: - self.lastDownloaded[filename] = 0 - self.currentlyDownloading = set() - self.downloadedCounter[filename] = 0 - self.getFile(filename) - super(PeriodicFileDownloader, self).__init__(*args, **kwargs) - - def _downloadFile(self, filename, url, f): - self.currentlyDownloading.add(filename) - try: - try: - infd = utils.web.getUrlFd(url) - except IOError, e: - self.log.warning('Error downloading %s: %s', url, e) - return - except utils.web.Error, e: - self.log.warning('Error downloading %s: %s', url, e) - return - confDir = conf.supybot.directories.data() - newFilename = os.path.join(confDir, utils.file.mktemp()) - outfd = file(newFilename, 'wb') - start = time.time() - s = infd.read(4096) - while s: - outfd.write(s) - s = infd.read(4096) - infd.close() - outfd.close() - self.log.info('Downloaded %s in %s seconds', - filename, time.time()-start) - self.downloadedCounter[filename] += 1 - self.lastDownloaded[filename] = time.time() - if f is None: - toFilename = os.path.join(confDir, filename) - if os.name == 'nt': - # Windows, grrr... - if os.path.exists(toFilename): - os.remove(toFilename) - os.rename(newFilename, toFilename) - else: - start = time.time() - f(newFilename) - total = time.time() - start - self.log.info('Function ran on %s in %s seconds', - filename, total) - finally: - self.currentlyDownloading.remove(filename) - - def getFile(self, filename): - if world.documenting: - return - (url, timeLimit, f) = self.periodicFiles[filename] - if time.time() - self.lastDownloaded[filename] > timeLimit and \ - filename not in self.currentlyDownloading: - self.log.info('Beginning download of %s', url) - args = (filename, url, f) - name = '%s #%s' % (filename, self.downloadedCounter[filename]) - t = threading.Thread(target=self._downloadFile, name=name, - args=(filename, url, f)) - t.setDaemon(True) - t.start() - world.threadsSpawned += 1 - - -class Debian(callbacks.Plugin, PeriodicFileDownloader): - threaded = True - periodicFiles = { - # This file is only updated once a week, so there's no sense in - # downloading a new one every day. - 'Contents-i386.gz': ('ftp://ftp.us.debian.org/' - 'debian/dists/unstable/Contents-i386.gz', - 604800, None) - } - contents = conf.supybot.directories.data.dirize('Contents-i386.gz') - def file(self, irc, msg, args, optlist, glob): - """[--{regexp,exact} ] [] - - Returns packages in Debian that includes files matching . If - --regexp is given, returns packages that include files matching the - given regexp. If --exact is given, returns packages that include files - matching exactly the string given. - """ - self.getFile('Contents-i386.gz') - # Make sure it's anchored, make sure it doesn't have a leading slash - # (the filenames don't have leading slashes, and people may not know - # that). - if not optlist and not glob: - raise callbacks.ArgumentError - if optlist and glob: - irc.error('You must specify either a glob or a regexp/exact ' - 'search, but not both.', Raise=True) - for (option, arg) in optlist: - if option == 'exact': - regexp = arg.lstrip('/') - elif option == 'regexp': - regexp = arg - if glob: - regexp = fnmatch.translate(glob.lstrip('/')) - regexp = regexp.rstrip('$') - regexp = ".*%s.* " % regexp - try: - re_obj = re.compile(regexp, re.I) - except re.error, e: - irc.error(format('Error in regexp: %s', e), Raise=True) - if self.registryValue('pythonZgrep'): - fd = gzip.open(self.contents) - r = imap(lambda tup: tup[0], - ifilter(lambda tup: tup[0], - imap(lambda line:(re_obj.search(line), line),fd))) - else: - try: - (r, w) = popen2.popen4(['zgrep', '-ie', regexp, self.contents]) - w.close() - except TypeError: - # We're on Windows. - irc.error('This command won\'t work on this platform. ' - 'If you think it should (i.e., you know that you ' - 'have a zgrep binary somewhere) then file a bug ' - 'about it at http://supybot.sf.net/ .', Raise=True) - packages = set() # Make packages unique - try: - for line in r: - if len(packages) > 100: - irc.error('More than 100 packages matched, ' - 'please narrow your search.', Raise=True) - try: - if hasattr(line, 'group'): # we're actually using - line = line.group(0) # pythonZgrep :( - (filename, pkg_list) = line.split() - if filename == 'FILE': - # This is the last line before the actual files. - continue - except ValueError: # Unpack list of wrong size. - continue # We've not gotten to the files yet. - packages.update(pkg_list.split(',')) - finally: - if hasattr(r, 'close'): - r.close() - if len(packages) == 0: - irc.reply('I found no packages with that file.') - else: - irc.reply(format('%L', sorted(packages))) - file = wrap(file, [getopts({'regexp':'regexpMatcher','exact':'something'}), - additional('glob')]) - - _debreflags = re.DOTALL | re.IGNORECASE - _deblistre = re.compile(r'

    Package ([^<]+)

    (.*?)', _debreflags) - def version(self, irc, msg, args, optlist, branch, package): - """[--exact] [{stable,testing,unstable,experimental}] - - Returns the current version(s) of a Debian package in the given branch - (if any, otherwise all available ones are displayed). If --exact is - specified, only packages whose name exactly matches - will be reported. - """ - url = 'http://packages.debian.org/cgi-bin/search_packages.pl?keywords'\ - '=%s&searchon=names&version=%s&release=all&subword=1' - for (option, _) in optlist: - if option == 'exact': - url = url.replace('&subword=1','') - responses = [] - if '*' in package: - irc.error('Wildcard characters can not be specified.', Raise=True) - package = utils.web.urlquote(package) - url %= (package, branch) - try: - html = utils.web.getUrl(url) - except utils.web.Error, e: - irc.error(format('I couldn\'t reach the search page (%s).', e), - Raise=True) - if 'is down at the moment' in html: - irc.error('Packages.debian.org is down at the moment. ' - 'Please try again later.', Raise=True) - pkgs = self._deblistre.findall(html) - if not pkgs: - irc.reply(format('No package found for %s (%s)', - utils.web.urlunquote(package), branch)) - else: - for pkg in pkgs: - pkgMatch = pkg[0] - soup = BeautifulSoup.BeautifulSoup() - soup.feed(pkg[1]) - liBranches = soup.fetch('li') - branches = [] - versions = [] - def branchVers(br): - vers = [b.next.string.strip() for b in br] - return [utils.str.rsplit(v, ':', 1)[0] for v in vers] - for li in liBranches: - branches.append(li.a.string) - versions.append(branchVers(li.fetch('br'))) - if branches and versions: - for pairs in zip(branches, versions): - branch = pairs[0] - ver = ', '.join(pairs[1]) - s = format('%s (%s)', pkgMatch, - ': '.join([branch, ver])) - responses.append(s) - resp = format('%i matches found: %s', - len(responses), '; '.join(responses)) - irc.reply(resp) - version = wrap(version, [getopts({'exact':''}), - optional(('literal', ('stable', 'testing', - 'unstable', 'experimental')), 'all'), - 'text']) - - _incomingRe = re.compile(r'
    ', re.I) - def incoming(self, irc, msg, args, optlist, globs): - """[--{regexp,arch} ] [ ...] - - Checks debian incoming for a matching package name. The arch - parameter defaults to i386; --regexp returns only those package names - that match a given regexp, and normal matches use standard *nix - globbing. - """ - predicates = [] - archPredicate = lambda s: ('_i386.' in s) - for (option, arg) in optlist: - if option == 'regexp': - predicates.append(r.search) - elif option == 'arch': - arg = '_%s.' % arg - archPredicate = lambda s, arg=arg: (arg in s) - predicates.append(archPredicate) - for glob in globs: - glob = fnmatch.translate(glob) - predicates.append(re.compile(glob).search) - packages = [] - try: - fd = utils.web.getUrlFd('http://incoming.debian.org/') - except utils.web.Error, e: - irc.error(str(e), Raise=True) - for line in fd: - m = self._incomingRe.search(line) - if m: - name = m.group(1) - if all(None, imap(lambda p: p(name), predicates)): - realname = utils.str.rsplit(name, '_', 1)[0] - packages.append(realname) - if len(packages) == 0: - irc.error('No packages matched that search.') - else: - irc.reply(format('%L', packages)) - incoming = thread(wrap(incoming, - [getopts({'regexp': 'regexpMatcher', - 'arch': 'something'}), - any('glob')])) - - def bold(self, s): - if self.registryValue('bold', dynamic.channel): - return ircutils.bold(s) - return s - - _update = re.compile(r' : ([^<]+) - - Reports various statistics (from http://packages.qa.debian.org/) about - . - """ - pkg = pkg.lower() - text = utils.web.getUrl('http://packages.qa.debian.org/%s/%s.html' % - (pkg[0], pkg)) - if "Error 404" in text: - irc.errorInvalid('source package name') - updated = None - m = self._update.search(text) - if m: - updated = m.group(1) - soup = BeautifulSoup.BeautifulSoup() - soup.feed(text) - pairs = zip(soup.fetch('td', {'class': 'labelcell'}), - soup.fetch('td', {'class': 'contentcell'})) - for (label, content) in pairs: - if label.string == 'Last version': - version = '%s: %s' % (self.bold(label.string), content.string) - elif label.string == 'Maintainer': - name = content.a.string - email = content.fetch('a')[1]['href'][7:] - maintainer = format('%s: %s %u', self.bold('Maintainer'), - name, utils.web.mungeEmail(email)) - elif label.string == 'All bugs': - bugsAll = format('%i Total', content.first('a').string) - elif label.string == 'Release Critical': - bugsRC = format('%i RC', content.first('a').string) - elif label.string == 'Important and Normal': - bugs = format('%i Important/Normal', - content.first('a').string) - elif label.string == 'Minor and Wishlist': - bugsMinor = format('%i Minor/Wishlist', - content.first('a').string) - elif label.string == 'Fixed and Pending': - bugsFixed = format('%i Fixed/Pending', - content.first('a').string) - elif label.string == 'Subscribers count': - subscribers = format('%s: %i', - self.bold('Subscribers'), content.string) - bugL = (bugsAll, bugsRC, bugs, bugsMinor, bugsFixed) - s = '. '.join((version, maintainer, subscribers, - '%s: %s' % (self.bold('Bugs'), '; '.join(bugL)))) - if updated: - s = 'As of %s, %s' % (updated, s) - irc.reply(s) - stats = wrap(stats, ['somethingWithoutSpaces']) - - _newpkgre = re.compile(r'
  • ]+>([^<]+)') - def new(self, irc, msg, args, section, glob): - """[{main,contrib,non-free}] [] - - Checks for packages that have been added to Debian's unstable branch - in the past week. If no glob is specified, returns a list of all - packages. If no section is specified, defaults to main. - """ - try: - fd = utils.web.getUrlFd( - 'http://packages.debian.org/unstable/newpkg_%s' % section) - except utils.web.Error, e: - irc.error(str(e), Raise=True) - packages = [] - for line in fd: - m = self._newpkgre.search(line) - if m: - m = m.group(1) - if fnmatch.fnmatch(m, glob): - packages.append(m) - fd.close() - if packages: - irc.reply(format('%L', packages)) - else: - irc.error('No packages matched that search.') - new = wrap(new, [optional(('literal', ('main', 'contrib', 'non-free')), - 'main'), - additional('glob', '*')]) - - _severity = re.compile(r'.*(?:severity set to `([^\']+)\'|' - r'severity:\s+([^<]+))', re.I) - _package = re.compile(r'Package: <[^>]+>([^<]+)<', re.I | re.S) - _reporter = re.compile(r'Reported by: <[^>]+>([^<]+)<', re.I | re.S) - _subject = re.compile(r'
    ([^<]+)', re.I | re.S) - _date = re.compile(r'Date: ([^;]+);', re.I | re.S) - _tags = re.compile(r'Tags: ([^<]+)', re.I) - _searches = (_package, _subject, _reporter, _date) - def bug(self, irc, msg, args, bug): - """ - - Returns a description of the bug with bug id . - """ - url = 'http://bugs.debian.org/%s' % bug - try: - text = utils.web.getUrl(url) - except utils.web.Error, e: - irc.error(str(e), Raise=True) - if "There is no record of Bug" in text: - irc.error('I could not find a bug report matching that number.', - Raise=True) - searches = map(lambda p: p.search(text), self._searches) - sev = self._severity.search(text) - tags = self._tags.search(text) - # This section should be cleaned up to ease future modifications - if all(None, searches): - L = map(self.bold, ('Package', 'Subject', 'Reported')) - resp = format('%s: %%s; %s: %%s; %s: by %%s on %%s', *L) - L = map(utils.web.htmlToText, map(lambda p: p.group(1), searches)) - resp = format(resp, *L) - if sev: - sev = filter(None, sev.groups()) - if sev: - sev = utils.web.htmlToText(sev[0]) - resp += format('; %s: %s', self.bold('Severity'), sev) - if tags: - resp += format('; %s: %s', self.bold('Tags'), tags.group(1)) - resp += format('; %u', url) - irc.reply(resp) - else: - irc.reply('I was unable to properly parse the BTS page.') - bug = wrap(bug, [('id', 'bug')]) - - _dpnRe = re.compile(r'"\+2">([^<]+) - - Turns into a 'debian package name' using - http://www.pigdog.com/features/dpn.html. - """ - url = r'http://www.pigdog.org/cgi_bin/dpn.phtml?name=%s' - try: - text = utils.web.getUrl(url % '+'.join(words)) - except utils.web.Error, e: - irc.error(str(e), Raise=True) - m = self._dpnRe.search(text) - if m is not None: - irc.reply(m.group(1)) - else: - irc.errorPossibleBug('Unable to parse webpage.') - debianize = wrap(debianize, [many('something')]) - - -Class = Debian - - -# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/Debian/test.py b/plugins/Debian/test.py deleted file mode 100644 index 8fc0b5c08..000000000 --- a/plugins/Debian/test.py +++ /dev/null @@ -1,92 +0,0 @@ -### -# Copyright (c) 2003-2005, James Vega -# 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. -### - -import os -import time - -from supybot.test import * - -class DebianTestCase(PluginTestCase): - plugins = ('Debian',) - timeout = 100 - cleanDataDir = False - fileDownloaded = False - if network: - def setUp(self, nick='test'): - PluginTestCase.setUp(self) - try: - datadir = conf.supybot.directories.data - if os.path.exists(datadir.dirize('Contents-i386.gz')): - pass - else: - print - print "Downloading files, this may take awhile." - filename = datadir.dirize('Contents-i386.gz') - while not os.path.exists(filename): - time.sleep(1) - print "Download complete." - print "Starting test ..." - self.fileDownloaded = True - except KeyboardInterrupt: - pass - - def testDebBugNoHtml(self): - self.assertNotRegexp('debian bug 287792', r'\') - - def testDebversion(self): - self.assertHelp('debian version') - self.assertRegexp('debian version lakjdfad', - r'^No package.*\(all\)') - self.assertRegexp('debian version unstable alkdjfad', - r'^No package.*\(unstable\)') - self.assertRegexp('debian version gaim', - r'\d+ matches found:.*gaim.*\(stable') - self.assertRegexp('debian version linux-wlan', - r'\d+ matches found:.*linux-wlan.*') - self.assertRegexp('debian version --exact linux-wlan', - r'^No package.*\(all\)') - self.assertError('debian version unstable') - - def testDebfile(self): - self.assertHelp('file') - if not self.fileDownloaded: - pass - self.assertRegexp('file --exact bin/gaim', r'net/gaim') - - def testDebincoming(self): - self.assertNotError('incoming') - - def testDebianize(self): - self.assertNotError('debianize supybot') - - def testDebstats(self): - self.assertNotError('stats supybot') - - -# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: From 79930a74357a814ada8ebfb59f288789e1fdc97b Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Mon, 1 Nov 2010 19:48:45 +0100 Subject: [PATCH 099/136] Bug fix in src/i18n.py --- src/i18n.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/i18n.py b/src/i18n.py index 4877ec32a..7edfeca90 100644 --- a/src/i18n.py +++ b/src/i18n.py @@ -311,6 +311,10 @@ def internationalizeDocstring(obj): 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__) - # We use _.__call__() instead of _() because of a pygettext warning. + try: + obj.__doc__=sys.modules[obj.__module__]._.__call__(obj.__doc__) + # We use _.__call__() instead of _() because of a pygettext warning. + except AttributeError: + # attribute '__doc__' of 'type' objects is not writable + pass return obj From 382bad4a1da127f98795523f8faa6764cea7fbea Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Mon, 1 Nov 2010 19:52:28 +0100 Subject: [PATCH 100/136] AutoMode: fix misspell in french locale --- plugins/AutoMode/locale/fr.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/AutoMode/locale/fr.po b/plugins/AutoMode/locale/fr.po index a866bb41a..53c063034 100644 --- a/plugins/AutoMode/locale/fr.po +++ b/plugins/AutoMode/locale/fr.po @@ -23,7 +23,7 @@ msgstr "Détermine si ce plugin est activé." msgid "" "Determines whether this plugin will automode\n" " owners." -msgstr "Détermine si ce pluginmettra des modes automatiques sur les owners." +msgstr "Détermine si ce plugin mettra des modes automatiques sur les owners." #: config.py:52 msgid "" From c0ff3c3f92b589365043023216c19dfcd6b536f6 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Mon, 1 Nov 2010 19:57:18 +0100 Subject: [PATCH 101/136] Bug fix in i18n.py (decorated commands without docstring raise an error) --- src/i18n.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/i18n.py b/src/i18n.py index 7edfeca90..a5eca10da 100644 --- a/src/i18n.py +++ b/src/i18n.py @@ -309,6 +309,8 @@ def internationalizeDocstring(obj): """Decorates functions and internationalize their docstring. Only useful for commands (commands' docstring is displayed on IRC)""" + if obj.__doc__ == None: + return obj if sys.modules[obj.__module__].__dict__.has_key('_'): internationalizedCommands.update({hash(obj): obj}) try: From 31d9d47751b708f04610040175390b95823eb851 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Mon, 1 Nov 2010 20:15:02 +0100 Subject: [PATCH 102/136] Remove debug message --- src/test.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test.py b/src/test.py index 2ff5fa465..e1b629e25 100644 --- a/src/test.py +++ b/src/test.py @@ -360,7 +360,6 @@ class PluginTestCase(SupyTestCase): return for cb in self.irc.callbacks: name = cb.name() - print " --- " + name if ((name in self._noTestDoc) and \ not name.lower() in self.__class__.__name__.lower()): continue From 02cb15d522a6e7987bd41120c1154da80edb6409 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Mon, 1 Nov 2010 20:18:44 +0100 Subject: [PATCH 103/136] Remove the 'testInternationalization' --- src/test.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/test.py b/src/test.py index e1b629e25..99d3514d8 100644 --- a/src/test.py +++ b/src/test.py @@ -371,12 +371,6 @@ class PluginTestCase(SupyTestCase): attr == callbacks.canonicalName(attr): self.failUnless(getattr(cb, attr, None).__doc__, '%s.%s has no help.' % (name, attr)) - def testInternationalization(self): - name = self.__class__.__module__[0:-len('.test')] - if self.__class__.__module__.startswith('supybot'): - return - self.failIf(hasattr(sys.modules[name], '_') == False, - '%s has no internationalizer.' % name) From 713aae90170122aac2b3786e7be07e8c992c93c5 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Tue, 2 Nov 2010 14:06:06 +0100 Subject: [PATCH 104/136] Change the version suffix --- scripts/supybot | 2 +- src/conf.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/supybot b/scripts/supybot index ec9b7ecfe..7435de239 100644 --- a/scripts/supybot +++ b/scripts/supybot @@ -125,7 +125,7 @@ def main(): log.info('Total CPU time taken: %s seconds.', user+system) log.info('No more Irc objects, exiting.') -version = '0.83.4.1+git+fr+testing' +version = '0.83.4.1+git+fr1' if __name__ == '__main__': parser = optparse.OptionParser(usage='Usage: %prog [options] configFile', version='Supybot %s' % version) diff --git a/src/conf.py b/src/conf.py index 880906118..fee98b7d8 100644 --- a/src/conf.py +++ b/src/conf.py @@ -42,7 +42,7 @@ _ = PluginInternationalization() ### # version: This should be pretty obvious. ### -version = '0.83.4.1+git+fr+testing' +version = '0.83.4.1+git+fr1' ### # *** The following variables are affected by command-line options. They are From 9bd66a7e7c4011b307fa52cec0d97f9ab7c360d4 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Wed, 3 Nov 2010 19:09:42 +0100 Subject: [PATCH 105/136] Plugin: fix two localization error --- plugins/Plugin/locale/fr.po | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/Plugin/locale/fr.po b/plugins/Plugin/locale/fr.po index 0c5d61a53..c5dd0eb4c 100644 --- a/plugins/Plugin/locale/fr.po +++ b/plugins/Plugin/locale/fr.po @@ -69,11 +69,11 @@ msgstr "plugin" #: plugin.py:92 msgid "The %q command is available in the %L %s." -msgstr "La commande %q est disponibles dans le(s) plugin %v." +msgstr "La commande %q est disponibles dans le(s) plugin(s) %L %v." #: plugin.py:95 msgid "There is no command %q." -msgstr "Il n'y a pas de commande q." +msgstr "Il n'y a pas de commande %q." #: plugin.py:100 msgid "" From 485c6161ee29a13f47f72f5491834ba3cb105092 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Wed, 3 Nov 2010 19:12:00 +0100 Subject: [PATCH 106/136] Plugin: fix a little localization error --- plugins/Plugin/locale/fr.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Plugin/locale/fr.po b/plugins/Plugin/locale/fr.po index c5dd0eb4c..d62ab426e 100644 --- a/plugins/Plugin/locale/fr.po +++ b/plugins/Plugin/locale/fr.po @@ -69,7 +69,7 @@ msgstr "plugin" #: plugin.py:92 msgid "The %q command is available in the %L %s." -msgstr "La commande %q est disponibles dans le(s) plugin(s) %L %v." +msgstr "La commande %q est disponibles dans le(s) plugin(s) %L%v." #: plugin.py:95 msgid "There is no command %q." From b1f0e058a805a695e20293f076c6ff6e2ed2b266 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sat, 6 Nov 2010 08:12:26 +0100 Subject: [PATCH 107/136] Fix two function misspell in locale/fr.py --- locale/fr.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/locale/fr.py b/locale/fr.py index 23bb731d2..2a6884ea1 100644 --- a/locale/fr.py +++ b/locale/fr.py @@ -40,7 +40,7 @@ def pluralize(s): lowered in ['bijou', 'caillou', 'chou', 'genou', 'hibou', 'joujou', 'pou']: return s + 'x' - elif lowered.endwith('al') and \ + elif lowered.endswith('al') and \ lowered not in ['bal', 'carnaval', 'chacal', 'festival', 'récital', 'régal', 'cal', 'étal', 'aval', 'caracal', 'val', 'choral', 'corral', 'galgal', 'gayal']: @@ -53,7 +53,7 @@ def pluralize(s): return s + 'x' elif lowered == 'pare-feu': return s - elif lowered.endwith('eu') and \ + elif lowered.endswith('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' From 323ffe1a1f2f723fdef99e172e9d11409342d65d Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Thu, 11 Nov 2010 12:01:56 +0100 Subject: [PATCH 108/136] i18n: Fix internationalization problems --- scripts/supybot | 3 +++ src/i18n.py | 60 +++++++++++++++++++++++++++++------------------- src/utils/str.py | 12 +++------- 3 files changed, 43 insertions(+), 32 deletions(-) diff --git a/scripts/supybot b/scripts/supybot index 7f1953776..5658c25ec 100644 --- a/scripts/supybot +++ b/scripts/supybot @@ -61,6 +61,7 @@ import textwrap started = time.time() import supybot +import supybot.i18n as i18n import supybot.utils as utils import supybot.registry as registry import supybot.questions as questions @@ -178,6 +179,7 @@ if __name__ == '__main__': docs/GETTING_STARTED and follow the instructions.""")) else: registryFilename = args.pop() + i18n.getLocaleFromRegistryFilename(registryFilename) try: # The registry *MUST* be opened before importing log or conf. registry.open(registryFilename) @@ -210,6 +212,7 @@ if __name__ == '__main__': sys.exit(-1) import supybot.conf as conf import supybot.world as world + i18n.import_conf() world.starting = True def closeRegistry(): diff --git a/src/i18n.py b/src/i18n.py index a5eca10da..773448d02 100644 --- a/src/i18n.py +++ b/src/i18n.py @@ -37,6 +37,7 @@ import re import sys import time import threading +conf = None # Don't import conf here ; because conf needs this module WAITING_FOR_MSGID = 1 @@ -47,16 +48,26 @@ IN_MSGSTR = 4 MSGID = 'msgid "' MSGSTR = 'msgstr "' +currentLocale = 'en' + +def getLocaleFromRegistryFilename(filename): + """Called by the 'supybot' script. Gets the locale name before conf is + loaded.""" + global currentLocale + for line in open(filename, 'r'): + if line.startswith('supybot.language: '): + currentLocale = line[len('supybot.language: '):] + def import_conf(): - import supybot.conf as conf - globals().update({'conf': conf}) + """Imports the conf into this module""" + global conf + conf = __import__('supybot.conf').conf conf.registerGlobalValue(conf.supybot, 'language', - conf.registry.String('en', """Determines the bot's default language. - Valid values are things like en, fr, de, etc.""")) - for key in i18nClasses: - i18nClasses[key].loadLocale() + conf.registry.String(currentLocale, """Determines the bot's default + language. Valid values are things like en, fr, de, etc.""")) def getPluginDir(plugin_name): + """Gets the directory of the given plugin""" filename = None try: filename = sys.modules[plugin_name].__file__ @@ -74,6 +85,8 @@ def getPluginDir(plugin_name): return def getLocalePath(name, localeName, extension): + """Gets the path of the locale file of the given plugin ('supybot' stands + for the core).""" if name != 'supybot': directory = getPluginDir(name) + 'locale' else: @@ -85,7 +98,15 @@ i18nClasses = {} internationalizedCommands = {} internationalizedFunctions = [] # No need to know there name -def reloadLocals(): +def reloadLocalesIfRequired(): + global currentLocale + if conf is None: + return + if currentLocale != conf.supybot.language(): + currentLocale = conf.supybot.language() + reloadLocales() + +def reloadLocales(): for pluginName in i18nClasses: i18nClasses[pluginName].loadLocale() for commandHash in internationalizedCommands: @@ -111,17 +132,12 @@ class _PluginInternationalization: self.name = name self.currentLocaleName = None i18nClasses.update({name: self}) - if name != 'supybot' and not 'conf' in globals(): - # if conf is loadable but not loaded - import_conf() 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' + if localeName is None: + localeName = currentLocale self.currentLocaleName = localeName self._loadL10nCode() @@ -184,14 +200,16 @@ class _PluginInternationalization: self._addToDatabase(untranslated, translated) def _addToDatabase(self, untranslated, translated): - untranslated = self._unescape(untranslated) + untranslated = self._unescape(untranslated, True) translated = self._unescape(translated) self.translations.update({untranslated: translated}) - def _unescape(self, string): + def _unescape(self, string, removeNewline=False): import supybot.utils as utils string = str.replace(string, '\\n', '\n') # gettext escapes the \n - string = utils.str.normalizeWhitespace(string, removeNewline=False) + string = str.replace(string, '\\"', '"') + string = str.replace(string, "\'", "'") + string = utils.str.normalizeWhitespace(string, removeNewline) return string def __call__(self, untranslated): @@ -200,12 +218,8 @@ class _PluginInternationalization: his is the function which is called when a plugin runs _()""" if untranslated.__class__ == internationalizedString: return untranslated._original - untranslated = self._unescape(untranslated) - if not 'conf' in globals(): - return untranslated - if self.currentLocaleName != conf.supybot.language(): - # If the locale has been changed - reloadLocals() + untranslated = self._unescape(untranslated, True) + reloadLocalesIfRequired() try: string = self._translate(untranslated) return self._addTracker(string, untranslated) diff --git a/src/utils/str.py b/src/utils/str.py index c624fe7bb..d410aa542 100644 --- a/src/utils/str.py +++ b/src/utils/str.py @@ -61,16 +61,10 @@ def rsplit(s, sep=None, maxsplit=-1): def normalizeWhitespace(s, removeNewline=True): """Normalizes the whitespace in a string; \s+ becomes one space.""" - beginning = s.startswith(' ') - ending = s.endswith(' ') if removeNewline: - s = ' '.join(s.split()) - else: - s = ' '.join(s.split(' ')) - if beginning: - s = ' ' + s - if ending: - s = s + ' ' + s = str.replace(s, '\n', '') + while ' ' in s: + s = str.replace(s, ' ', ' ') return s def distance(s, t): From 11f7033ee0ddc06ecd0acc84827e16ea83bd3287 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Thu, 11 Nov 2010 12:36:47 +0100 Subject: [PATCH 109/136] Factoids: Internationalize a config variable --- plugins/Factoids/config.py | 2 +- plugins/Factoids/messages.pot | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/Factoids/config.py b/plugins/Factoids/config.py index 6fdce802f..4f167865f 100644 --- a/plugins/Factoids/config.py +++ b/plugins/Factoids/config.py @@ -61,7 +61,7 @@ conf.registerChannelValue(Factoids, 'replyWhenInvalidCommand', commands by searching for a factoid; basically making the whatis unnecessary when you want all factoids for a given key."""))) conf.registerChannelValue(Factoids, 'format', - FactoidFormat('$key could be $value.', _("""Determines the format of + FactoidFormat(_('$key could be $value.'), _("""Determines the format of the response given when a factoid's value is requested. All the standard substitutes apply, in addition to "$key" for the factoid's key and "$value" for the factoid's value."""))) diff --git a/plugins/Factoids/messages.pot b/plugins/Factoids/messages.pot index 2c40af2e8..5bcd6f497 100644 --- a/plugins/Factoids/messages.pot +++ b/plugins/Factoids/messages.pot @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2010-10-17 10:59+CEST\n" +"POT-Creation-Date: 2010-11-11 12:37+CET\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -44,6 +44,10 @@ msgid "" " unnecessary when you want all factoids for a given key." msgstr "" +#: config.py:64 +msgid "$key could be $value." +msgstr "" + #: config.py:64 msgid "" "Determines the format of\n" From 4d957e858e21bdc4121467dca33bf28980966659 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Thu, 11 Nov 2010 12:39:13 +0100 Subject: [PATCH 110/136] Factoids: localize a string --- plugins/Factoids/locale/fr.po | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/Factoids/locale/fr.po b/plugins/Factoids/locale/fr.po index 79f1b413f..a9d4b6531 100644 --- a/plugins/Factoids/locale/fr.po +++ b/plugins/Factoids/locale/fr.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: Supybot-fr\n" -"POT-Creation-Date: 2010-10-17 10:59+CEST\n" +"POT-Creation-Date: 2010-11-11 12:37+CET\n" "PO-Revision-Date: \n" "Last-Translator: Valentin Lorentz \n" "Language-Team: Supybot-fr \n" @@ -41,6 +41,10 @@ msgid "" " unnecessary when you want all factoids for a given key." msgstr "Détermine si le bot répondra aux commandes invalides lors de la recherche d'une factoid ; permet simplement de rendre la commande 'whatis' inutile lorsque vous voulez toutes les factoids d'un clef donnée." +#: config.py:64 +msgid "$key could be $value." +msgstr "$key semble être $value." + #: config.py:64 msgid "" "Determines the format of\n" From 0998c2a43f320e9001ef40a2b56ebae175002e0a Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sun, 14 Nov 2010 14:36:02 +0100 Subject: [PATCH 111/136] Fix bug in src/commands.py (forgotten comma) --- src/commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands.py b/src/commands.py index 8f3961bbf..bc54ce884 100644 --- a/src/commands.py +++ b/src/commands.py @@ -199,7 +199,7 @@ def getFloat(irc, msg, args, state, type=_('floating point number')): def getPositiveInt(irc, msg, args, state, *L): getInt(irc, msg, args, state, - p=lambda i: i>0, type=_('positive integer') *L) + p=lambda i: i>0, type=_('positive integer'), *L) def getNonNegativeInt(irc, msg, args, state, *L): getInt(irc, msg, args, state, From 415f79d9a53924a3322c1e36621fd42e31194bec Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Wed, 17 Nov 2010 16:44:34 +0100 Subject: [PATCH 112/136] Later: fix localization problem --- plugins/Later/locale/fr.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Later/locale/fr.po b/plugins/Later/locale/fr.po index 0ce99e82c..f798ef713 100644 --- a/plugins/Later/locale/fr.po +++ b/plugins/Later/locale/fr.po @@ -104,5 +104,5 @@ msgstr "Il n'y a pas de note pour %r" #: plugin.py:182 msgid "Sent %s: <%s> %s" -msgstr "Envoyé le %s : " +msgstr "Envoyé le %s : <%> %s" From a618f111aaed91700272a62e82ff3f082933bf03 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Wed, 17 Nov 2010 19:21:36 +0100 Subject: [PATCH 113/136] Later: fix localization problem --- plugins/Later/locale/fr.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Later/locale/fr.po b/plugins/Later/locale/fr.po index f798ef713..b03053f05 100644 --- a/plugins/Later/locale/fr.po +++ b/plugins/Later/locale/fr.po @@ -104,5 +104,5 @@ msgstr "Il n'y a pas de note pour %r" #: plugin.py:182 msgid "Sent %s: <%s> %s" -msgstr "Envoyé le %s : <%> %s" +msgstr "Envoyé le %s : <%s> %s" From 05560299d9fd247148ccf207c8d1c56ff8ddd30e Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Wed, 17 Nov 2010 21:40:11 +0100 Subject: [PATCH 114/136] Later: fix localisation problem --- plugins/Later/locale/fr.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Later/locale/fr.po b/plugins/Later/locale/fr.po index f798ef713..b03053f05 100644 --- a/plugins/Later/locale/fr.po +++ b/plugins/Later/locale/fr.po @@ -104,5 +104,5 @@ msgstr "Il n'y a pas de note pour %r" #: plugin.py:182 msgid "Sent %s: <%s> %s" -msgstr "Envoyé le %s : <%> %s" +msgstr "Envoyé le %s : <%s> %s" From 3fafd31f51d5ec1af40c08b04c15e12dcd981ab0 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Fri, 19 Nov 2010 17:00:55 +0100 Subject: [PATCH 115/136] Remove redundant spaces in src/i18n.py --- src/i18n.py | 372 ++++++++++++++++++++++++++-------------------------- 1 file changed, 186 insertions(+), 186 deletions(-) diff --git a/src/i18n.py b/src/i18n.py index 0cbd573bb..72720acc5 100644 --- a/src/i18n.py +++ b/src/i18n.py @@ -55,43 +55,43 @@ def getLocaleFromRegistryFilename(filename): loaded.""" global currentLocale for line in open(filename, 'r'): - if line.startswith('supybot.language: '): - currentLocale = line[len('supybot.language: '):] + if line.startswith('supybot.language: '): + currentLocale = line[len('supybot.language: '):] def import_conf(): """Imports the conf into this module""" global conf conf = __import__('supybot.conf').conf conf.registerGlobalValue(conf.supybot, 'language', - conf.registry.String(currentLocale, """Determines the bot's default + conf.registry.String(currentLocale, """Determines the bot's default language. Valid values are things like en, fr, de, etc.""")) def getPluginDir(plugin_name): """Gets the directory of the given plugin""" filename = None try: - filename = sys.modules[plugin_name].__file__ + filename = sys.modules[plugin_name].__file__ except KeyError: # It sometimes happens with Owner - pass + pass if filename == None: - filename = sys.modules['supybot.plugins.' + plugin_name].__file__ + filename = sys.modules['supybot.plugins.' + plugin_name].__file__ if filename.endswith(".pyc"): - filename = filename[0:-1] - + filename = filename[0:-1] + allowed_files = ['__init__.py', 'config.py', 'plugin.py', 'test.py'] for allowed_file in allowed_files: - if filename.endswith(allowed_file): - return filename[0:-len(allowed_file)] - return + if filename.endswith(allowed_file): + return filename[0:-len(allowed_file)] + return def getLocalePath(name, localeName, extension): """Gets the path of the locale file of the given plugin ('supybot' stands for the core).""" if name != 'supybot': - directory = getPluginDir(name) + 'locale' + directory = getPluginDir(name) + 'locale' else: - import ansi # Any Supybot plugin could fit - directory = ansi.__file__[0:-len('ansi.pyc')] + 'locale' + import ansi # Any Supybot plugin could fit + directory = ansi.__file__[0:-len('ansi.pyc')] + 'locale' return '%s/%s.%s' % (directory, localeName, extension) i18nClasses = {} @@ -101,213 +101,213 @@ internationalizedFunctions = [] # No need to know there name def reloadLocalesIfRequired(): global currentLocale if conf is None: - return + return if currentLocale != conf.supybot.language(): - currentLocale = conf.supybot.language() - reloadLocales() + currentLocale = conf.supybot.language() + reloadLocales() def reloadLocales(): for pluginName in i18nClasses: - i18nClasses[pluginName].loadLocale() + i18nClasses[pluginName].loadLocale() for commandHash in internationalizedCommands: - internationalizeDocstring(internationalizedCommands[commandHash]) + internationalizeDocstring(internationalizedCommands[commandHash]) for function in internationalizedFunctions: - function.loadLocale() - + function.loadLocale() + i18nSupybot = None def PluginInternationalization(name='supybot'): # This is a proxy that prevents having several objects for the same plugin if i18nClasses.has_key(name): - return i18nClasses[name] + return i18nClasses[name] else: - return _PluginInternationalization(name) + return _PluginInternationalization(name) class _PluginInternationalization: """Internationalization managment for a plugin.""" def __init__(self, name='supybot'): - self.name = name - self.currentLocaleName = None - i18nClasses.update({name: self}) - self.loadLocale() - + self.name = name + self.currentLocaleName = None + i18nClasses.update({name: self}) + self.loadLocale() + def loadLocale(self, localeName=None): - """(Re)loads the locale used by this class.""" - if localeName is None: - localeName = currentLocale - self.currentLocaleName = localeName - - self._loadL10nCode() + """(Re)loads the locale used by this class.""" + if localeName is None: + localeName = currentLocale + self.currentLocaleName = localeName - 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 = {} + 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 = {} def _parse(self, translationFile): - """A .po files parser. + """A .po files parser. + + Give it a file object.""" + step = WAITING_FOR_MSGID + self.translations = {} + for line in translationFile: + 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_MSGSTR + else: + self._addToDatabase(untranslated, data) + step = WAITING_FOR_MSGID + + + elif step is IN_MSGSTR and line.startswith('"') and \ + line.endswith('"'): + translated += line[1:-1] + elif step is IN_MSGSTR: # the MSGSTR is finished + step = WAITING_FOR_MSGID + if translated == '': + translated = untranslated + self._addToDatabase(untranslated, translated) - Give it a file object.""" - step = WAITING_FOR_MSGID - self.translations = {} - for line in translationFile: - 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_MSGSTR - else: - self._addToDatabase(untranslated, data) - step = WAITING_FOR_MSGID - - - elif step is IN_MSGSTR and line.startswith('"') and \ - line.endswith('"'): - translated += line[1:-1] - elif step is IN_MSGSTR: # the MSGSTR is finished - step = WAITING_FOR_MSGID - if translated == '': - translated = untranslated - self._addToDatabase(untranslated, translated) - def _addToDatabase(self, untranslated, translated): - untranslated = self._unescape(untranslated, True) - translated = self._unescape(translated) - self.translations.update({untranslated: translated}) - + untranslated = self._unescape(untranslated, True) + translated = self._unescape(translated) + self.translations.update({untranslated: translated}) + def _unescape(self, string, removeNewline=False): - import supybot.utils as utils - string = str.replace(string, '\\n', '\n') # gettext escapes the \n - string = str.replace(string, '\\"', '"') - string = str.replace(string, "\'", "'") - string = utils.str.normalizeWhitespace(string, removeNewline) - return string - + import supybot.utils as utils + string = str.replace(string, '\\n', '\n') # gettext escapes the \n + string = str.replace(string, '\\"', '"') + string = str.replace(string, "\'", "'") + string = utils.str.normalizeWhitespace(string, removeNewline) + return string + def __call__(self, untranslated): - """Main function. + """Main function. + + his is the function which is called when a plugin runs _()""" + if untranslated.__class__ == internationalizedString: + return untranslated._original + untranslated = self._unescape(untranslated, True) + reloadLocalesIfRequired() + try: + string = self._translate(untranslated) + return self._addTracker(string, untranslated) + except KeyError: + pass + return untranslated - his is the function which is called when a plugin runs _()""" - if untranslated.__class__ == internationalizedString: - return untranslated._original - untranslated = self._unescape(untranslated, True) - reloadLocalesIfRequired() - try: - string = self._translate(untranslated) - return self._addTracker(string, untranslated) - except KeyError: - pass - return untranslated - def _translate(self, string): - """Translate the string. + """Translate the string. + + C the string internationalizer if any; else, use the local database""" + if string.__class__ == internationalizedString: + return string._internationalizer(string.untranslated) + else: + return self.translations[string] - C the string internationalizer if any; else, use the local database""" - if string.__class__ == internationalizedString: - return string._internationalizer(string.untranslated) - else: - return self.translations[string] - def _addTracker(self, string, untranslated): - """Add a kind of 'tracker' on the string, in order to keep the - untranslated string (used when changing the locale)""" - if string.__class__ == internationalizedString: - return string - else: - string = internationalizedString(string) - string._original = untranslated - string._internationalizer = self - return string + """Add a kind of 'tracker' on the string, in order to keep the + untranslated string (used when changing the locale)""" + if string.__class__ == internationalizedString: + return string + else: + string = internationalizedString(string) + string._original = untranslated + string._internationalizer = self + return string def _loadL10nCode(self): - """Open the file containing the code specific to this locale, and - load its functions.""" - 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 - + """Open the file containing the code specific to this locale, and + load its functions.""" + 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. + """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') - It contains functions that needs to by fully (code + strings) - localized""" - if self.name != 'supybot': - return - return getLocalePath('supybot', self.currentLocaleName, 'py') - def localizeFunction(self, name): - """Returns the localized version of the function. + """Returns the localized version of the function. + + 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] - 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 internationalizeFunction(self, name): - """Decorates functions and internationalize their code. + """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) + 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) + 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() + self.__call__ = self._internationalizer.localizeFunction(self._name) + if self.__call__ == None: + self.restore() def restore(self): - self.__call__ = self._origin + self.__call__ = self._origin class internationalizedString(str): """Simple subclass to str, that allow to add attributes. Also used to @@ -319,13 +319,13 @@ def internationalizeDocstring(obj): Only useful for commands (commands' docstring is displayed on IRC)""" if obj.__doc__ == None: - return obj + return obj if sys.modules[obj.__module__].__dict__.has_key('_'): - internationalizedCommands.update({hash(obj): obj}) - try: - obj.__doc__=sys.modules[obj.__module__]._.__call__(obj.__doc__) - # We use _.__call__() instead of _() because of a pygettext warning. - except AttributeError: - # attribute '__doc__' of 'type' objects is not writable - pass + internationalizedCommands.update({hash(obj): obj}) + try: + obj.__doc__=sys.modules[obj.__module__]._.__call__(obj.__doc__) + # We use _.__call__() instead of _() because of a pygettext warning. + except AttributeError: + # attribute '__doc__' of 'type' objects is not writable + pass return obj From 6629c8d48fe270e01d5e7afc0c6713c9193d876d Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Fri, 19 Nov 2010 17:10:47 +0100 Subject: [PATCH 116/136] Add @reloadlocale to Owner plugin --- plugins/Owner/plugin.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugins/Owner/plugin.py b/plugins/Owner/plugin.py index 73d2707a1..e825de208 100644 --- a/plugins/Owner/plugin.py +++ b/plugins/Owner/plugin.py @@ -42,6 +42,7 @@ else: 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 @@ -599,6 +600,12 @@ class Owner(callbacks.Plugin): self.reload(irc, msg, [plugin.name()]) # This makes the replySuccess. unrename = wrap(unrename, ['plugin']) + def reloadlocale(self, irc, msg, args): + """takes no argument + + Reloads the locale of the bot.""" + i18n.reloadLocales() + irc.replySuccess() Class = Owner From 13b194a4fec17dffcf5877e98b9e229759d5d7c0 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Fri, 26 Nov 2010 23:19:05 +0100 Subject: [PATCH 117/136] Fix internationalisation bug in Config plugin --- plugins/Config/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Config/plugin.py b/plugins/Config/plugin.py index 6ad3bdf8e..8f669dc76 100644 --- a/plugins/Config/plugin.py +++ b/plugins/Config/plugin.py @@ -156,7 +156,7 @@ class Config(callbacks.Plugin): Searches for in the current configuration variables. """ L = [] - for (name, _) in conf.supybot.getValues(getChildren=True): + for (name, x) in conf.supybot.getValues(getChildren=True): if word in name.lower(): possibleChannel = registry.split(name)[-1] if not ircutils.isChannel(possibleChannel): From f2a0b60e797311ada840075541b5d7568e839668 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sun, 28 Nov 2010 17:47:38 +0100 Subject: [PATCH 118/136] Fix unicode bug --- src/utils/str.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/str.py b/src/utils/str.py index 033547c2c..2997bfac6 100644 --- a/src/utils/str.py +++ b/src/utils/str.py @@ -61,6 +61,7 @@ def rsplit(s, sep=None, maxsplit=-1): def normalizeWhitespace(s, removeNewline=True): """Normalizes the whitespace in a string; \s+ becomes one space.""" + s = str(s) if removeNewline: s = str.replace(s, '\n', '') while ' ' in s: From 96ea659030432e1860f01aafc9dbafc7cd2bba1c Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Thu, 9 Dec 2010 19:33:35 +0100 Subject: [PATCH 119/136] Add SSL support for Socket driver --- src/drivers/Socket.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/drivers/Socket.py b/src/drivers/Socket.py index 6db605a59..4cc2d6194 100644 --- a/src/drivers/Socket.py +++ b/src/drivers/Socket.py @@ -37,6 +37,10 @@ from __future__ import division import time import select import socket +try: + import ssl +except: + pass import supybot.log as log import supybot.conf as conf @@ -61,10 +65,11 @@ class SocketDriver(drivers.IrcDriver, drivers.ServersMixin): self.writeCheckTime = None self.nextReconnectTime = None self.resetDelay() - # Only connect to non-SSL servers - if self.networkGroup.get('ssl').value: + if self.networkGroup.get('ssl').value and not globals().has_key('ssl'): drivers.log.error('The Socket driver can not connect to SSL ' - 'servers. Try the Twisted driver instead.') + 'servers for your Python version. Try the ' + 'Twisted driver instead, or install a Python' + 'version that supports SSL (2.6 and greater).') else: self.connect() @@ -139,6 +144,12 @@ class SocketDriver(drivers.IrcDriver, drivers.ServersMixin): self.irc.feedMsg(msg) except socket.timeout: pass + except ssl.SSLError as e: + if e.args[0] == 'The read operation timed out': + pass + else: + self._handleSocketError(e) + return except socket.error, e: self._handleSocketError(e) return @@ -175,6 +186,9 @@ class SocketDriver(drivers.IrcDriver, drivers.ServersMixin): try: self.conn.connect(server) self.conn.settimeout(conf.supybot.drivers.poll()) + if getattr(conf.supybot.networks, self.irc.network).ssl(): + assert globals().has_key('ssl') + self.conn = ssl.wrap_socket(self.conn) self.connected = True self.resetDelay() except socket.error, e: From a58e2705161cd35d50ce10817d33db25e4c720a7 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sun, 12 Dec 2010 14:17:09 +0100 Subject: [PATCH 120/136] Fix issue with Pypy --- src/__init__.py | 2 +- src/drivers/Socket.py | 2 +- src/dynamicScope.py | 2 +- src/utils/__init__.py | 10 +++++----- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/__init__.py b/src/__init__.py index 3871a8940..4a1fdab74 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -34,7 +34,7 @@ import dynamicScope import supybot.utils as utils -__builtins__['format'] = utils.str.format +(__builtins__ if isinstance(__builtins__, dict) else __builtins__.__dict__)['format'] = utils.str.format class Author(object): def __init__(self, name=None, nick=None, email=None, **kwargs): diff --git a/src/drivers/Socket.py b/src/drivers/Socket.py index 4cc2d6194..d992792e0 100644 --- a/src/drivers/Socket.py +++ b/src/drivers/Socket.py @@ -144,7 +144,7 @@ class SocketDriver(drivers.IrcDriver, drivers.ServersMixin): self.irc.feedMsg(msg) except socket.timeout: pass - except ssl.SSLError as e: + except ssl.SSLError, e: if e.args[0] == 'The read operation timed out': pass else: diff --git a/src/dynamicScope.py b/src/dynamicScope.py index 6b4675cad..e1fd97fd2 100644 --- a/src/dynamicScope.py +++ b/src/dynamicScope.py @@ -47,6 +47,6 @@ class DynamicScope(object): def __setattr__(self, name, value): self._getLocals(name)[name] = value -__builtins__['dynamic'] = DynamicScope() +(__builtins__ if isinstance(__builtins__, dict) else __builtins__.__dict__)['dynamic'] = DynamicScope() # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/src/utils/__init__.py b/src/utils/__init__.py index 7de7a425d..c32e23ef9 100644 --- a/src/utils/__init__.py +++ b/src/utils/__init__.py @@ -54,14 +54,14 @@ def force(x): return x() else: return x -__builtins__['force'] = force +(__builtins__ if isinstance(__builtins__, dict) else __builtins__.__dict__)['force'] = force if sys.version_info < (2, 4, 0): def reversed(L): """Iterates through a sequence in reverse.""" for i in xrange(len(L) - 1, -1, -1): yield L[i] - __builtins__['reversed'] = reversed + (__builtins__ if isinstance(__builtins__, dict) else __builtins__.__dict__)['reversed'] = reversed def sorted(iterable, cmp=None, key=None, reversed=False): L = list(iterable) @@ -74,7 +74,7 @@ if sys.version_info < (2, 4, 0): L.reverse() return L - __builtins__['sorted'] = sorted + (__builtins__ if isinstance(__builtins__, dict) else __builtins__.__dict__)['sorted'] = sorted import operator def itemgetter(i): @@ -86,8 +86,8 @@ if sys.version_info < (2, 4, 0): operator.attrgetter = attrgetter import sets - __builtins__['set'] = sets.Set - __builtins__['frozenset'] = sets.ImmutableSet + (__builtins__ if isinstance(__builtins__, dict) else __builtins__.__dict__)['set'] = sets.Set + (__builtins__ if isinstance(__builtins__, dict) else __builtins__.__dict__)['frozenset'] = sets.ImmutableSet import socket # Some socket modules don't have sslerror, so we'll just make it an error. From 49dfa69b91c37a11f588af6130c0a313283d712e Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sun, 12 Dec 2010 14:33:36 +0100 Subject: [PATCH 121/136] Fix actually compatibility with non-ssl envirronments --- src/drivers/Socket.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/drivers/Socket.py b/src/drivers/Socket.py index d992792e0..d1ca4ee8a 100644 --- a/src/drivers/Socket.py +++ b/src/drivers/Socket.py @@ -39,8 +39,10 @@ import select import socket try: import ssl + SSLError = ssl.SSLError except: - pass + class SSLError(Exception): + pass import supybot.log as log import supybot.conf as conf @@ -144,7 +146,7 @@ class SocketDriver(drivers.IrcDriver, drivers.ServersMixin): self.irc.feedMsg(msg) except socket.timeout: pass - except ssl.SSLError, e: + except SSLError, e: if e.args[0] == 'The read operation timed out': pass else: From b3ffbdf951caf8c5760b6b66817ad94b064a9483 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sun, 12 Dec 2010 15:00:50 +0100 Subject: [PATCH 122/136] Fix localization issue --- locale/fr.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locale/fr.po b/locale/fr.po index 519c6cde7..1bc929a46 100644 --- a/locale/fr.po +++ b/locale/fr.po @@ -39,7 +39,7 @@ msgstr "\"|\" avec rien ne le suivant. Je ne peux évidtemment pas faire un pipe #: callbacks.py:515 msgid "%s is not a valid %s." -msgstr "%s n'est pas un %s valide." +msgstr "%s n'est pas du type '%s'." #: callbacks.py:517 msgid "That's not a valid %s." From a739511b7329c290bc5164e71593ae5435d14dab Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sun, 12 Dec 2010 15:03:13 +0100 Subject: [PATCH 123/136] Internationalize two strings in Config --- plugins/Config/messages.pot | 10 +++++++++- plugins/Config/plugin.py | 4 ++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/plugins/Config/messages.pot b/plugins/Config/messages.pot index bc578f015..91f5aa6d6 100644 --- a/plugins/Config/messages.pot +++ b/plugins/Config/messages.pot @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2010-10-16 12:34+CEST\n" +"POT-Creation-Date: 2010-12-12 15:02+CET\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -15,6 +15,14 @@ msgstr "" "Generated-By: pygettext.py 1.5\n" +#: plugin.py:103 +msgid "configuration variable" +msgstr "" + +#: plugin.py:109 +msgid "settable configuration variable" +msgstr "" + #: plugin.py:136 #, docstring msgid "" diff --git a/plugins/Config/plugin.py b/plugins/Config/plugin.py index 8f669dc76..d248f4bcd 100644 --- a/plugins/Config/plugin.py +++ b/plugins/Config/plugin.py @@ -100,13 +100,13 @@ def getConfigVar(irc, msg, args, state): state.args.append(group) del args[0] except registry.InvalidRegistryName, e: - state.errorInvalid('configuration variable', str(e)) + state.errorInvalid(_('configuration variable'), str(e)) addConverter('configVar', getConfigVar) def getSettableConfigVar(irc, msg, args, state): getConfigVar(irc, msg, args, state) if not hasattr(state.args[-1], 'set'): - state.errorInvalid('settable configuration variable', + state.errorInvalid(_('settable configuration variable'), state.args[-1]._name) addConverter('settableConfigVar', getSettableConfigVar) From 01018b902e5508d6f43daf5990235994bb55894c Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sun, 12 Dec 2010 15:05:00 +0100 Subject: [PATCH 124/136] Localize the two new strings of Config in French --- plugins/Config/locale/fr.po | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/plugins/Config/locale/fr.po b/plugins/Config/locale/fr.po index 2627b7553..9eb4cb258 100644 --- a/plugins/Config/locale/fr.po +++ b/plugins/Config/locale/fr.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: Supybot-fr\n" -"POT-Creation-Date: 2010-10-16 12:34+CEST\n" +"POT-Creation-Date: 2010-12-12 15:02+CET\n" "PO-Revision-Date: \n" "Last-Translator: Valentin Lorentz \n" "Language-Team: Supybot-fr \n" @@ -13,6 +13,14 @@ msgstr "" "X-Poedit-Country: France\n" "X-Poedit-SourceCharset: ASCII\n" +#: plugin.py:103 +msgid "configuration variable" +msgstr "variable de configuration" + +#: plugin.py:109 +msgid "settable configuration variable" +msgstr "variable de configuration modifiable" + #: plugin.py:136 msgid "" "\n" From 5629b4585c9b61591f7ef824e5493e11c66671a0 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Wed, 22 Dec 2010 18:15:46 +0100 Subject: [PATCH 125/136] Fix bolding for help strings (because of i18n) --- src/i18n.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/i18n.py b/src/i18n.py index 72720acc5..fb610ea2f 100644 --- a/src/i18n.py +++ b/src/i18n.py @@ -213,10 +213,11 @@ class _PluginInternationalization: his is the function which is called when a plugin runs _()""" if untranslated.__class__ == internationalizedString: return untranslated._original - untranslated = self._unescape(untranslated, True) + escapedUntranslated = self._unescape(untranslated, True) + untranslated = self._unescape(untranslated, False) reloadLocalesIfRequired() try: - string = self._translate(untranslated) + string = self._translate(escapedUntranslated) return self._addTracker(string, untranslated) except KeyError: pass From d008f9738359ee2844bef73c90a33a7df9a04467 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Thu, 23 Dec 2010 15:51:06 +0100 Subject: [PATCH 126/136] Fix @Games monologue french translation --- plugins/Games/locale/fr.po | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/Games/locale/fr.po b/plugins/Games/locale/fr.po index c7d66051a..30c7aa2db 100644 --- a/plugins/Games/locale/fr.po +++ b/plugins/Games/locale/fr.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "POT-Creation-Date: 2010-10-17 13:16+CEST\n" -"PO-Revision-Date: 2010-10-28 13:25+0100\n" +"PO-Revision-Date: 2010-12-23 15:46+0100\n" "Last-Translator: Valentin Lorentz \n" "Language-Team: French\n" "MIME-Version: 1.0\n" @@ -129,7 +129,7 @@ msgstr "[]\\Retourne le nombre de lignes consécutives que vous avez écr #: plugin.py:167 msgid "Your current monologue is at least %n long." -msgstr "Votre monologue actuel est au moins long de %n lignes." +msgstr "Votre monologue actuel est au moins long de %n." #: plugin.py:168 msgid "line" From f45e0b01ace7b8e0ade6124a433d710014b8ad3a Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Thu, 23 Dec 2010 19:56:23 +0100 Subject: [PATCH 127/136] Fix @help monologue french translation --- plugins/Games/locale/fr.po | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plugins/Games/locale/fr.po b/plugins/Games/locale/fr.po index 30c7aa2db..bdeabee12 100644 --- a/plugins/Games/locale/fr.po +++ b/plugins/Games/locale/fr.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "POT-Creation-Date: 2010-10-17 13:16+CEST\n" -"PO-Revision-Date: 2010-12-23 15:46+0100\n" +"PO-Revision-Date: 2010-12-23 19:55+0100\n" "Last-Translator: Valentin Lorentz \n" "Language-Team: French\n" "MIME-Version: 1.0\n" @@ -125,7 +125,10 @@ msgid "" " 'monologue' is). is only necessary if the message isn't sent\n" " in the channel itself.\n" " " -msgstr "[]\\Retourne le nombre de lignes consécutives que vous avez écrites sur le sans être interrompu par qui que ce soit. n'est nécessaire que si le message n'est pas envoyé sur le canal lui-même." +msgstr "" +"[]\n" +"\n" +"Retourne le nombre de lignes consécutives que vous avez écrites sur le sans être interrompu par qui que ce soit. n'est nécessaire que si le message n'est pas envoyé sur le canal lui-même." #: plugin.py:167 msgid "Your current monologue is at least %n long." From 6e6fd58ea7061b327e8145f1fa4d43fe3c221a3b Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sat, 1 Jan 2011 17:24:13 +0100 Subject: [PATCH 128/136] Fix RSS encoding problem --- plugins/RSS/plugin.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/RSS/plugin.py b/plugins/RSS/plugin.py index fbf442e37..eb0c7af11 100644 --- a/plugins/RSS/plugin.py +++ b/plugins/RSS/plugin.py @@ -264,8 +264,12 @@ class RSS(callbacks.Plugin): def _getConverter(self, feed): toText = utils.web.htmlToText if 'encoding' in feed: - return lambda s: toText(s).strip().encode(feed['encoding'], - 'replace') + def conv(s): + try: + return toText(s).strip().encode(feed['encoding'],'replace') + except UnicodeEncodeError: + return toText(s.encode('utf-8', 'ignore')).strip() + return conv else: return lambda s: toText(s).strip() From 35822866589d2e4c240479472ecca58ad1cb3ea8 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sun, 2 Jan 2011 09:31:07 +0100 Subject: [PATCH 129/136] Fix Factoids --- plugins/Factoids/plugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/Factoids/plugin.py b/plugins/Factoids/plugin.py index bb4c68452..7a2d8c21f 100644 --- a/plugins/Factoids/plugin.py +++ b/plugins/Factoids/plugin.py @@ -284,7 +284,7 @@ class Factoids(callbacks.Plugin, plugins.ChannelDBHandler): if cursor.rowcount == 0: irc.error(_('There is no such factoid.')) elif cursor.rowcount == 1 or number is True: - (id, _) = cursor.fetchone() + (id, foo) = cursor.fetchone() cursor.execute("""DELETE FROM factoids WHERE key_id=%s""", id) cursor.execute("""DELETE FROM keys WHERE key LIKE %s""", key) db.commit() @@ -293,7 +293,7 @@ class Factoids(callbacks.Plugin, plugins.ChannelDBHandler): if number is not None: results = cursor.fetchall() try: - (_, id) = results[number-1] + (foo, id) = results[number-1] except IndexError: irc.error(_('Invalid factoid number.')) return From 4649188b9632bdfeb6c24446104eab3ed9070631 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sun, 2 Jan 2011 13:22:54 +0100 Subject: [PATCH 130/136] Fix detection of .42 domains --- src/utils/web.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/web.py b/src/utils/web.py index 6a1fa9361..8935f7ef5 100644 --- a/src/utils/web.py +++ b/src/utils/web.py @@ -57,7 +57,7 @@ _octet = r'(?:2(?:[0-4]\d|5[0-5])|1\d\d|\d{1,2})' _ipAddr = r'%s(?:\.%s){3}' % (_octet, _octet) # Base domain regex off RFC 1034 and 1738 _label = r'[0-9a-z][-0-9a-z]*[0-9a-z]?' -_domain = r'%s(?:\.%s)*\.[a-z][-0-9a-z]*[a-z]?' % (_label, _label) +_domain = r'%s(?:\.%s)*\.[0-9a-z][-0-9a-z]+' % (_label, _label) _urlRe = r'(\w+://(?:%s|%s)(?::\d+)?(?:/[^\])>\s]*)?)' % (_domain, _ipAddr) urlRe = re.compile(_urlRe, re.I) _httpUrlRe = r'(https?://(?:%s|%s)(?::\d+)?(?:/[^\])>\s]*)?)' % (_domain, From d273ac164e78b851b5aaa370fc2924187ef6443a Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Wed, 5 Jan 2011 17:22:34 +0100 Subject: [PATCH 131/136] Fix Network localization --- plugins/Network/locale/fr.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Network/locale/fr.po b/plugins/Network/locale/fr.po index 30d554177..586ca448d 100644 --- a/plugins/Network/locale/fr.po +++ b/plugins/Network/locale/fr.po @@ -82,7 +82,7 @@ msgid "" msgstr "" " [ ...]\n" "\n" -"Envoie la au bot (avez des arguments) sur le ." +"Envoie la au bot (avec les arguments) sur le ." #: plugin.py:210 msgid "is an op on %L" From f13a2c7ee434b79745f50155d1adf2e2c1bbada1 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sat, 8 Jan 2011 13:35:43 +0100 Subject: [PATCH 132/136] Fix translations --- plugins/Alias/locale/fr.po | 2 +- plugins/Misc/locale/fr.po | 6 +----- plugins/Seen/locale/fr.po | 2 +- plugins/String/locale/fr.po | 2 +- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/plugins/Alias/locale/fr.po b/plugins/Alias/locale/fr.po index e849f619f..87c3bacbe 100644 --- a/plugins/Alias/locale/fr.po +++ b/plugins/Alias/locale/fr.po @@ -84,7 +84,7 @@ msgid "" msgstr "" " \n" "\n" -"Défini un alias qui exécute . L' peut être dans le standard \"commande argument [commandeimbriquee argument]\". Les arguments donnés à l'alias doivent être donnés dans l'ordre. Vous pouvez utiliser $1, $2, etc pour symboliser les arguments obligatoires qui seront donnés à l'alias, et @1, @2, etc pour symboliser ceux optionnels. $* signifit simplement *tous* les arguments restants, et ne peut être combiné avec des arguments optionnels." +"Défini un alias qui exécute . L' peut être dans le standard \"commande argument [commandeimbriquee argument]\". Les arguments donnés à l'alias doivent être donnés dans l'ordre. Vous pouvez utiliser $1, $2, etc pour symboliser les arguments obligatoires qui seront donnés à l'alias, et @1, @2, etc pour symboliser ceux optionnels. $* signifie simplement *tous* les arguments restants, et ne peut être combiné avec des arguments optionnels." #: plugin.py:315 msgid "" diff --git a/plugins/Misc/locale/fr.po b/plugins/Misc/locale/fr.po index 485bfaf78..521275059 100644 --- a/plugins/Misc/locale/fr.po +++ b/plugins/Misc/locale/fr.po @@ -74,7 +74,7 @@ msgstr "Il n'y a pas de plugin privé." #: plugin.py:153 msgid "That plugin exists, but has no commands. This probably means that it has some configuration variables that can be changed in order to modify its behavior. Try \"config list supybot.plugins.%s\" to see what configuration variables it has." -msgstr "Ce plugin existe, mais n'a pas de commande. Cela signifit probablement qu'il a des variables de configuration qui peuvent être changés pour modifier son comportement. Essayez \"config list supybot.plugins.%s\" pour voir quelles variables de configuration il a." +msgstr "Ce plugin existe, mais n'a pas de commande. Cela signifie probablement qu'il a des variables de configuration qui peuvent être changés pour modifier son comportement. Essayez \"config list supybot.plugins.%s\" pour voir quelles variables de configuration il a." #: plugin.py:164 msgid "" @@ -214,22 +214,18 @@ msgstr "" "Dit le au . Utile si vous utilisez des commandes imbriquées." #: plugin.py:396 -#, fuzzy msgid "Dude, just give the command. No need for the tell." msgstr "Mec, contentes-toi de me donner la commande. Pas besoin d'utiliser 'tell'." #: plugin.py:401 -#, fuzzy msgid "You just told me, why should I tell myself?" msgstr "Vous venez de me le dire, pourquoi devrais-je me le dire moi-même ?" #: plugin.py:406 -#, fuzzy msgid "I haven't seen %s, I'll let you do the telling." msgstr "Je n'ai pas vu %s, je vous laisse lui dire." #: plugin.py:411 -#, fuzzy msgid "%s wants me to tell you: %s" msgstr "%s veut que je vous dise : %s" diff --git a/plugins/Seen/locale/fr.po b/plugins/Seen/locale/fr.po index 899041f15..a675a3251 100644 --- a/plugins/Seen/locale/fr.po +++ b/plugins/Seen/locale/fr.po @@ -101,7 +101,7 @@ msgid "" msgstr "" "[] \n" "\n" -"Retourne la dernière fois que a été vu et la dernière fois que a été vu parler. Cela recherche dans la base de données des utilisateurs vus, ce qui signifit que si le nick n'était pas reconnu comme l'utilisateur , il n'est pas considéré comme vu. n'est nécessaire que si le message n'est pas envoyé sur le canal lui-même." +"Retourne la dernière fois que a été vu et la dernière fois que a été vu parler. Cela recherche dans la base de données des utilisateurs vus, ce qui signifie que si le nick n'était pas reconnu comme l'utilisateur , il n'est pas considéré comme vu. n'est nécessaire que si le message n'est pas envoyé sur le canal lui-même." #: plugin.py:305 msgid "" diff --git a/plugins/String/locale/fr.po b/plugins/String/locale/fr.po index 6dc911991..6e57e5625 100644 --- a/plugins/String/locale/fr.po +++ b/plugins/String/locale/fr.po @@ -23,7 +23,7 @@ msgid "" " and more time. Using nested commands, strings can get quite large, hence\n" " this variable, to limit the size of arguments passed to the levenshtein\n" " command." -msgstr "Détermine la taille maximum d'une chaîne donnée à la commande 'levenshtein'. Cette commande utiliser un algorithme O(m*n), ce qui signifit que pour une chaîne de taille 256, il peut prendre 1,5 seconde à se finir ; pour les chaînes de taille 384, il peut en prendre 4, et ainsi de suite. En utilisant des commandes inmbriquées, les chaînes peuvent devenir très grandes, donc, utilisez cette variable pour limiter la taille des arguments passés à la commande 'levenshtein'." +msgstr "Détermine la taille maximum d'une chaîne donnée à la commande 'levenshtein'. Cette commande utiliser un algorithme O(m*n), ce qui signifie que pour une chaîne de taille 256, il peut prendre 1,5 seconde à se finir ; pour les chaînes de taille 384, il peut en prendre 4, et ainsi de suite. En utilisant des commandes inmbriquées, les chaînes peuvent devenir très grandes, donc, utilisez cette variable pour limiter la taille des arguments passés à la commande 'levenshtein'." #: plugin.py:46 msgid "" From a1f093defcdbbdddf04c4e3203568dcd022b8d39 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sat, 22 Jan 2011 09:51:06 +0100 Subject: [PATCH 133/136] Ignore vim temporary files --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index b41eb555e..cdcd23ad8 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,6 @@ test-logs *.pyc docs/_build docs/plugins +*.swp +*.swo +*~ From d29ca0a227e47d8a680e32b73c4d77be3f19c5b4 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sat, 22 Jan 2011 09:54:53 +0100 Subject: [PATCH 134/136] Import Time plugin from Supybot-plugin repo --- plugins/Time/__init__.py | 4 +++- plugins/Time/locale/fr.po | 42 +++++++++++++++++++++++++++++---------- plugins/Time/messages.pot | 38 ++++++++++++++++++++++++++--------- plugins/Time/plugin.py | 20 +++++++++++++++++++ plugins/Time/test.py | 4 ++++ 5 files changed, 88 insertions(+), 20 deletions(-) diff --git a/plugins/Time/__init__.py b/plugins/Time/__init__.py index f74a9b4a2..6cf97ccd7 100644 --- a/plugins/Time/__init__.py +++ b/plugins/Time/__init__.py @@ -42,7 +42,9 @@ __author__ = supybot.authors.jemfinch # This is a dictionary mapping supybot.Author instances to lists of # contributions. -__contributors__ = {} +__contributors__ = {'tztime': supybot.Author('Valentin Lorentz', 'ProgVal', + 'progval@gmail.com')} + import config import plugin diff --git a/plugins/Time/locale/fr.po b/plugins/Time/locale/fr.po index 40929781a..e2c10e807 100644 --- a/plugins/Time/locale/fr.po +++ b/plugins/Time/locale/fr.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: Supybot-fr\n" -"POT-Creation-Date: 2010-10-20 09:38+CEST\n" +"POT-Creation-Date: 2010-11-17 09:46+CET\n" "PO-Revision-Date: \n" "Last-Translator: Valentin Lorentz \n" "Language-Team: Supybot-fr \n" @@ -21,7 +21,7 @@ msgid "" " the empty string, the timestamp will not be shown." msgstr "Détermine la chaîne de format pour les timestamps. Référez-vous à la documentation du module Python time pour voir quels formats sont acceptés. Si vous définissez cette variable à une chaîne vide, le timestamp ne sera pas affiché." -#: plugin.py:60 +#: plugin.py:61 msgid "" "[y] [w] [d] [h] [m] [s]\n" "\n" @@ -36,7 +36,7 @@ msgstr "" "\n" "Retourne le nombre de secondes de la date donnée. Un exemple d'utilisation est \"seconds 2h 30m\", ce qui retournera 9000. Utile pour planifier des évènement à un certain nombre de secondes dans le futur." -#: plugin.py:95 +#: plugin.py:96 msgid "" "