From 6901e7f4d0e0cbd999787702a3fa55fbe0428c3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jannes=20H=C3=B6ke?= Date: Tue, 24 May 2016 15:49:23 +0200 Subject: [PATCH] implement plurals, update de_DE translation to use plurals --- bot.py | 58 +++++---- internationalization.py | 175 ++++++++++++++++++++++++++++ locales/__init__.py | 21 ++++ locales/available.py | 37 ++++++ locales/de_DE/LC_MESSAGES/unobot.po | 66 +++++++---- locales/unobot.pot | 59 ++++++---- results.py | 37 +++--- settings.py | 45 +++---- simple_commands.py | 33 ++++-- utils.py | 112 +----------------- 10 files changed, 410 insertions(+), 233 deletions(-) create mode 100644 internationalization.py create mode 100644 locales/available.py diff --git a/bot.py b/bot.py index 125a5d0..3dead69 100644 --- a/bot.py +++ b/bot.py @@ -37,11 +37,12 @@ from utils import display_name import card as c from errors import (NoGameInChatError, LobbyClosedError, AlreadyJoinedError, NotEnoughPlayersError, DeckEmptyError) -from utils import _, __, send_async, answer_async, user_locale, game_locales, \ - error, TIMEOUT +from utils import send_async, answer_async, error, TIMEOUT from shared_vars import botan, gm, updater, dispatcher +from internationalization import _, __, user_locale, game_locales +import simple_commands +import settings -import simple_commands, settings from simple_commands import help @@ -136,11 +137,12 @@ def leave_game(bot, update): except NotEnoughPlayersError: gm.end_game(chat, user) - send_async(bot, chat.id, text=__("Game ended!", game.translate)) + send_async(bot, chat.id, text=__("Game ended!", multi=game.translate)) else: send_async(bot, chat.id, - text=__("Okay. Next Player: {name}", game.translate).format( + text=__("Okay. Next Player: {name}", + multi=game.translate).format( name=display_name(game.current_player.user)), reply_to_message_id=update.message.message_id) @@ -197,10 +199,11 @@ def status_update(bot, update): pass except NotEnoughPlayersError: gm.end_game(chat, user) - send_async(bot, chat.id, text=__("Game ended!", game.translate)) + send_async(bot, chat.id, text=__("Game ended!", + multi=game.translate)) else: send_async(bot, chat.id, text=__("Removing {name} from the game", - game.translate) + multi=game.translate) .format(name=display_name(user))) @@ -236,7 +239,7 @@ def start_game(bot, update, args): __("First player: {name}\n" "Use /close to stop people from joining the game.\n" "Enable multi-translations with /enable_translations", - game.translate) + multi=game.translate) .format(name=display_name(game.current_player.user))) @run_async @@ -410,9 +413,12 @@ def skip_player(bot, update): delta = (now - started).seconds if delta < skipped_player.waiting_time: + n = skipped_player.waiting_time - delta send_async(bot, chat.id, - text=_("Please wait {time} seconds") - .format(time=(skipped_player.waiting_time - delta)), + text=_("Please wait {time} second", + "Please wait {time} seconds", + n) + .format(time=n), reply_to_message_id=update.message.message_id) elif skipped_player.waiting_time > 0: @@ -423,11 +429,17 @@ def skip_player(bot, update): except DeckEmptyError: pass + n = skipped_player.waiting_time send_async(bot, chat.id, text=__("Waiting time to skip this player has " + "been reduced to {time} second.\n" + "Next player: {name}", + "Waiting time to skip this player has " "been reduced to {time} seconds.\n" - "Next player: {name}", game.translate) - .format(time=skipped_player.waiting_time, + "Next player: {name}", + n, + multi=game.translate) + .format(time=n, name=display_name(next_player.user))) game.turn() @@ -437,7 +449,7 @@ def skip_player(bot, update): send_async(bot, chat.id, text=__("{name1} was skipped four times in a row " "and has been removed from the game.\n" - "Next player: {name2}", game.translate) + "Next player: {name2}", multi=game.translate) .format(name1=display_name(skipped_player.user), name2=display_name(next_player.user))) @@ -445,7 +457,7 @@ def skip_player(bot, update): send_async(bot, chat.id, text=__("{name} was skipped four times in a row " "and has been removed from the game.\n" - "The game ended.", game.translate) + "The game ended.", multi=game.translate) .format(name=display_name(skipped_player.user))) gm.end_game(chat.id, skipped_player.user) @@ -459,7 +471,6 @@ def reply_to_query(bot, update): Builds the result list for inline queries and answers to the client. """ results = list() - playable = list() switch = None try: @@ -476,7 +487,7 @@ def reply_to_query(bot, update): elif user_id == game.current_player.user.id: if game.choosing_color: add_choose_color(results, game) - add_other_cards(playable, player, results, game) + add_other_cards(player, results, game) else: if not player.drew: add_draw(player, results) @@ -541,7 +552,7 @@ def process_result(bot, update): return elif int(anti_cheat) != last_anti_cheat: send_async(bot, chat.id, - text=__("Cheat attempt by {name}", game.translate) + text=__("Cheat attempt by {name}", multi=game.translate) .format(name=display_name(player.user))) return elif result_id == 'call_bluff': @@ -560,7 +571,7 @@ def process_result(bot, update): if game in gm.chatid_games.get(chat.id, list()): send_async(bot, chat.id, - text=__("Next player: {name}", game.translate) + text=__("Next player: {name}", multi=game.translate) .format(name=display_name(game.current_player.user))) @@ -572,7 +583,7 @@ def reset_waiting_time(bot, player): player.waiting_time = 90 send_async(bot, chat.id, text=__("Waiting time for {name} has been reset to 90 " - "seconds", player.game.translate) + "seconds", multi=player.game.translate) .format(name=display_name(player.user))) @@ -598,7 +609,7 @@ def do_play_card(bot, player, result_id): if len(player.cards) == 0: send_async(bot, chat.id, - text=__("{name} won!", game.translate) + text=__("{name} won!", multi=game.translate) .format(name=user.first_name)) if us.stats: @@ -612,7 +623,8 @@ def do_play_card(bot, player, result_id): try: gm.leave_game(user, chat) except NotEnoughPlayersError: - send_async(bot, chat.id, text=__("Game ended!", game.translate)) + send_async(bot, chat.id, + text=__("Game ended!", multi=game.translate)) us2 = UserSetting.get(id=game.current_player.user.id) if us2 and us2.stats: @@ -636,7 +648,7 @@ def do_draw(bot, player): except DeckEmptyError: send_async(bot, player.game.chat.id, text=__("There are no more cards in the deck.", - game.translate)) + multi=game.translate)) if (game.last_card.value == c.DRAW_TWO or game.last_card.special == c.DRAW_FOUR) and \ @@ -694,6 +706,8 @@ dispatcher.add_handler(CommandHandler('enable_translations', dispatcher.add_handler(CommandHandler('disable_translations', disable_translations)) dispatcher.add_handler(CommandHandler('skip', skip_player)) +simple_commands.register() +settings.register() dispatcher.add_handler(MessageHandler([Filters.status_update], status_update)) dispatcher.add_error_handler(error) diff --git a/internationalization.py b/internationalization.py new file mode 100644 index 0000000..3eafa75 --- /dev/null +++ b/internationalization.py @@ -0,0 +1,175 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Telegram bot to play UNO in group chats +# Copyright (c) 2016 Jannes Höke +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + + +import gettext +from functools import wraps +import logging + +from locales import available_locales +from database import db_session +from user_setting import UserSetting +from shared_vars import gm + +GETTEXT_DOMAIN = 'unobot' +GETTEXT_DIR = 'locales' + + +class _Underscore(object): + """Class to emulate flufl.i18n behaviour, but with plural support""" + def __init__(self): + self.translators = { + locale: gettext.GNUTranslations( + open(gettext.find( + GETTEXT_DOMAIN, GETTEXT_DIR, languages=[locale] + ), 'rb') + ) + for locale + in available_locales.keys() + if locale != 'en_US' # No translation file for en_US + } + self.locale_stack = list() + + def push(self, locale): + self.locale_stack.append(locale) + + def pop(self): + if self.locale_stack: + return self.locale_stack.pop() + else: + return None + + @property + def code(self): + if self.locale_stack: + return self.locale_stack[-1] + else: + return None + + def __call__(self, singular, plural=None, n=1, locale=None): + if not locale: + locale = self.locale_stack[-1] + + if locale not in self.translators.keys(): + if n is 1: + return singular + else: + return plural + + translator = self.translators[locale] + + if plural is None: + return translator.gettext(singular) + else: + return translator.ngettext(singular, plural, n) + +_ = _Underscore() + + +def __(singular, plural=None, n=1, multi=False): + """Translates text into all locales on the stack""" + translations = list() + + if not multi: + _.push('en_US') + translations.append(_(singular)) + _.pop() + + else: + for locale in _.locale_stack: + translation = _(singular, plural, n, locale) + logging.info(translation) + + if translation not in translations: + translations.append(translation) + + return '\n'.join(translations) + + +def user_locale(func): + @wraps(func) + @db_session + def wrapped(bot, update, *pargs, **kwargs): + user, chat = _user_chat_from_update(update) + + with db_session: + us = UserSetting.get(id=user.id) + + if us: + _.push(us.lang) + else: + _.push('en_US') + + result = func(bot, update, *pargs, **kwargs) + _.pop() + return result + return wrapped + + +def game_locales(func): + @wraps(func) + @db_session + def wrapped(bot, update, *pargs, **kwargs): + user, chat = _user_chat_from_update(update) + player = gm.player_for_user_in_chat(user, chat) + locales = list() + + if player: + for player in player.game.players: + us = UserSetting.get(id=player.user.id) + + if us: + loc = us.lang + else: + loc = 'en_US' + + if loc in locales: + continue + + _.push(loc) + locales.append(loc) + + result = func(bot, update, *pargs, **kwargs) + + while _.code: + _.pop() + + return result + return wrapped + + +def _user_chat_from_update(update): + + try: + user = update.message.from_user + chat = update.message.chat + except (NameError, AttributeError): + try: + user = update.inline_query.from_user + chat = gm.userid_current[user.id].game.chat + except KeyError: + chat = None + except (NameError, AttributeError): + try: + user = update.chosen_inline_result.from_user + chat = gm.userid_current[user.id].game.chat + except (NameError, AttributeError): + chat = None + + return user, chat diff --git a/locales/__init__.py b/locales/__init__.py index e69de29..612d158 100644 --- a/locales/__init__.py +++ b/locales/__init__.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Telegram bot to play UNO in group chats +# Copyright (c) 2016 Jannes Höke +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + + +from .available import available_locales \ No newline at end of file diff --git a/locales/available.py b/locales/available.py new file mode 100644 index 0000000..5c42711 --- /dev/null +++ b/locales/available.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Telegram bot to play UNO in group chats +# Copyright (c) 2016 Jannes Höke +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + + +"""Defines a dict of all available locales to the language name""" + +OFFSET = 127462 - ord('A') + + +def flag(code): + return chr(ord(code[0]) + OFFSET) + chr(ord(code[1]) + OFFSET) + + +available_locales = {'en_US': flag('US') + ' English (US)', + 'de_DE': flag('DE') + ' Deutsch (DE)', + 'es_ES': flag('ES') + ' Español (ES)', + 'id_ID': flag('ID') + ' Bahasa Indonesia', + 'it_IT': flag('IT') + ' Italiano', + 'pt_BR': flag('BR') + ' Português Brasileiro', + 'zh_HK': flag('HK') + ' 廣東話', + 'zh_TW': flag('TW') + ' 中文(香港)'} diff --git a/locales/de_DE/LC_MESSAGES/unobot.po b/locales/de_DE/LC_MESSAGES/unobot.po index 0df04c7..c3847db 100644 --- a/locales/de_DE/LC_MESSAGES/unobot.po +++ b/locales/de_DE/LC_MESSAGES/unobot.po @@ -29,7 +29,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: utf-8\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Gtranslator 2.91.6\n" #: bot.py:60 @@ -229,17 +229,22 @@ msgstr "Du spielst kein Spiel in dieser Gruppe." #: bot.py:400 #, python-format -msgid "Please wait {time} seconds" -msgstr "Bitte warte {time} Sekunden" +msgid "Please wait {time} second" +msgid_plural "Please wait {time} seconds" +msgstr[0] "Bitte warte {time} Sekunde" +msgstr[1] "Bitte warte {time} Sekunden" #: bot.py:413 #, python-format -msgid "" -"Waiting time to skip this player has been reduced to {time} seconds.\n" +msgid "Waiting time to skip this player has been reduced to {time} second.\n" "Next player: {name}" -msgstr "" -"Die Wartezeit um diesen Spieler zu überspringen wurde auf {time} Sekunden " -"reduziert.\n" +msgid_plural "Waiting time to skip this player has been reduced to {time} seconds.\n" +"Next player: {name}" +msgstr[0] "Die Wartezeit um diesen Spieler zu überspringen wurde auf {time} " +"Sekunde reduziert.\n" +"Nächster Spieler: {name}" +msgstr[1] "Die Wartezeit um diesen Spieler zu überspringen wurde auf {time} " +"Sekunden reduziert.\n" "Nächster Spieler: {name}" #: bot.py:424 @@ -313,8 +318,10 @@ msgid "Choose Color" msgstr "Wähle Farbe" #: results.py:56 -msgid "Cards (tap for game state):" -msgstr "Karten (tippe für Spielinfo):" +msgid "Last card (tap for game state):" +msgid_plural "Cards (tap for game state):" +msgstr[0] "Letzte Karte (tippe für Spielinfo):" +msgstr[1] "Karten (tippe für Spielinfo):" #: results.py:60 results.py:123 results.py:165 msgid "Current player: {name}" @@ -325,13 +332,17 @@ msgid "Last card: {card}" msgstr "Letzte Karte: {card}" #: results.py:62 results.py:125 results.py:168 -msgid "Players: {player_list}" -msgstr "Spieler: {player_list}" +msgid "Player: {player_list}" +msgid_plural "Players: {player_list}" +msgstr[0] "Spieler: {player_list}" +msgstr[1] "Spieler: {player_list}" #: results.py:72 #, python-format -msgid "{name} ({number} cards)" -msgstr "{name} ({number} Karten)" +msgid "{name} ({number} card)" +msgid_plural "{name} ({number} cards)" +msgstr[0] "{name} ({number} Karte)" +msgstr[1] "{name} ({number} Karten)" #: results.py:81 msgid "You are not playing" @@ -355,11 +366,10 @@ msgstr "Starte das Spiel mit /start" #: results.py:108 #, python-format -msgid "Drawing 1 card" -msgstr "Zieht 1 Karte" - -msgid "Drawing {number} cards" -msgstr "Zieht {number} Karten" +msgid "Drawing {number} card" +msgid_plural "Drawing {number} cards" +msgstr[0] "Ich ziehe {number} Karte" +msgstr[1] "Ich ziehe {number} Karten" #: results.py:136 msgid "Pass" @@ -413,16 +423,22 @@ msgstr "Du hast die Spiel-Statistiken nicht aktiviert. Aktiviere sie, mit dem " "/settings-Kommando in einem privaten Chat mit dem Bot." #: simple_commands.py -msgid "{number} games played" -msgstr "{number} gespielte Spiele" +msgid "{number} game played" +msgid_plural "{number} games played" +msgstr[0] "{number} gespieltes Spiel" +msgstr[1] "{number} gespielte Spiele" #: simple_commands.py -msgid "{number} first places" -msgstr "{number}x 1. Platz" +msgid "{number} first place" +msgid_plural "{number} first places" +msgstr[0] "{number} mal 1. Platz" +msgstr[1] "{number} mal 1. Platz" #: simple_commands.py -msgid "{number} cards played" -msgstr "{number} gespielte Karten" +msgid "{number} card played" +msgid_plural "{number} cards played" +msgstr[0] "{number} gespielte Karte" +msgstr[1] "{number} gespielte Karten" #: utils.py diff --git a/locales/unobot.pot b/locales/unobot.pot index 1d00e72..a002baf 100644 --- a/locales/unobot.pot +++ b/locales/unobot.pot @@ -29,6 +29,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: utf-8\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" #: bot.py:60 @@ -184,14 +185,19 @@ msgstr "" #: bot.py:400 #, python-format -msgid "Please wait {time} seconds" -msgstr "" +msgid "Please wait {time} second" +msgid_plural "Please wait {time} seconds" +msgstr[0] "" +msgstr[1] "" #: bot.py:413 #, python-format -msgid "Waiting time to skip this player has been reduced to {time} seconds.\n" +msgid "Waiting time to skip this player has been reduced to {time} second.\n" "Next player: {name}" -msgstr "" +msgid_plural "Waiting time to skip this player has been reduced to {time} seconds.\n" +"Next player: {name}" +msgstr[0] "" +msgstr[1] "" #: bot.py:424 #, python-format @@ -256,8 +262,10 @@ msgid "Choose Color" msgstr "" #: results.py:56 -msgid "Cards (tap for game state):" -msgstr "" +msgid "Last card (tap for game state):" +msgid_plural "Cards (tap for game state):" +msgstr[0] "" +msgstr[1] "" #: results.py:60 results.py:123 results.py:165 msgid "Current player: {name}" @@ -268,13 +276,17 @@ msgid "Last card: {card}" msgstr "" #: results.py:62 results.py:125 results.py:168 -msgid "Players: {player_list}" -msgstr "" +msgid "Player: {player_list}" +msgid_plural "Players: {player_list}" +msgstr[0] "" +msgstr[1] "" #: results.py:72 #, python-format -msgid "{name} ({number} cards)" -msgstr "" +msgid "{name} ({number} card)" +msgid_plural "{name} ({number} cards)" +msgstr[0] "" +msgstr[1] "" #: results.py:81 msgid "You are not playing" @@ -295,11 +307,10 @@ msgstr "" #: results.py:108 #, python-format -msgid "Drawing 1 card" -msgstr "" - -msgid "Drawing {number} cards" -msgstr "" +msgid "Drawing {number} card" +msgid_plural "Drawing {number} cards" +msgstr[0] "" +msgstr[1] "" #: results.py:136 msgid "Pass" @@ -352,16 +363,22 @@ msgid "You did not enable statistics. Use /settings in " msgstr "" #: simple_commands.py -msgid "{number} games played" -msgstr "" +msgid "{number} game played" +msgid_plural "{number} games played" +msgstr[0] "" +msgstr[1] "" #: simple_commands.py -msgid "{number} first places" -msgstr "" +msgid "{number} first place" +msgid_plural "{number} first places" +msgstr[0] "" +msgstr[1] "" #: simple_commands.py -msgid "{number} cards played" -msgstr "" +msgid "{number} card played" +msgid_plural "{number} cards played" +msgstr[0] "" +msgstr[1] "" #: utils.py diff --git a/results.py b/results.py index 5d98be7..1c656fd 100644 --- a/results.py +++ b/results.py @@ -26,8 +26,8 @@ from telegram import InlineQueryResultArticle, InputTextMessageContent, \ InlineQueryResultCachedSticker as Sticker import card as c -from utils import display_color, display_color_group, display_name, \ - list_subtract, _, __ +from utils import display_color, display_color_group, display_name +from internationalization import _, __ def add_choose_color(results, game): @@ -44,17 +44,16 @@ def add_choose_color(results, game): ) -def add_other_cards(playable, player, results, game): +def add_other_cards(player, results, game): """Add hand cards when choosing colors""" - if not playable: - playable = list() results.append( InlineQueryResultArticle( "hand", - title=_("Cards (tap for game state):"), - description=', '.join([repr(card) for card in - list_subtract(player.cards, playable)]), + title=_("Card (tap for game state):", + "Cards (tap for game state):", + len(player.cards)), + description=', '.join([repr(card) for card in player.cards]), input_message_content=game_info(game) ) ) @@ -62,7 +61,9 @@ def add_other_cards(playable, player, results, game): def player_list(game): """Generate list of player strings""" - return [_("{name} ({number} cards)") + return [_("{name} ({number} card)", + "{name} ({number} cards)", + len(player.cards)) .format(name=player.user.first_name, number=len(player.cards)) for player in game.players] @@ -101,10 +102,9 @@ def add_draw(player, results): Sticker( "draw", sticker_file_id=c.STICKERS['option_draw'], input_message_content= - InputTextMessageContent(__('Drawing 1 card', player.game.translate) - if n == 1 else - __('Drawing {number} cards', - player.game.translate) + InputTextMessageContent(__('Drawing {number} card', + 'Drawing {number} cards', n, + multi=player.game.translate) .format(number=n)) ) ) @@ -127,8 +127,9 @@ def add_pass(results, game): results.append( Sticker( "pass", sticker_file_id=c.STICKERS['option_pass'], - input_message_content=InputTextMessageContent(__('Pass', - game.translate)) + input_message_content=InputTextMessageContent( + __('Pass', multi=game.translate) + ) ) ) @@ -141,7 +142,7 @@ def add_call_bluff(results, game): sticker_file_id=c.STICKERS['option_bluff'], input_message_content= InputTextMessageContent(__("I'm calling your bluff!", - game.translate)) + multi=game.translate)) ) ) @@ -168,6 +169,8 @@ def game_info(game): "\n" + _("Last card: {card}").format(card=repr(game.last_card)) + "\n" + - _("Players: {player_list}") + _("Player: {player_list}", + "Players: {player_list}", + len(players)) .format(player_list=" -> ".join(players)) ) \ No newline at end of file diff --git a/settings.py b/settings.py index 20bf7c1..d10bf38 100644 --- a/settings.py +++ b/settings.py @@ -18,31 +18,14 @@ # along with this program. If not, see . -import logging - from telegram import ReplyKeyboardMarkup, Emoji from telegram.ext import CommandHandler, RegexHandler from utils import send_async from user_setting import UserSetting -from utils import _, user_locale from shared_vars import dispatcher - -OFFSET = 127462 - ord('A') - - -def flag(code): - return chr(ord(code[0]) + OFFSET) + chr(ord(code[1]) + OFFSET) - - -available_locales = [['en_US - ' + flag('US') + ' English (US)'], - ['de_DE - ' + flag('DE') + ' Deutsch (DE)'], - ['es_ES - ' + flag('ES') + ' Español (ES)'], - ['id_ID - ' + flag('ID') + ' Bahasa Indonesia'], - ['it_IT - ' + flag('IT') + ' Italiano'], - ['pt_BR - ' + flag('BR') + ' Português Brasileiro'], - ['zh_HK - ' + flag('HK') + ' 廣東話'], - ['zh_TW - ' + flag('TW') + ' 中文(香港)']] +from locales import available_locales +from internationalization import _, user_locale @user_locale @@ -83,8 +66,11 @@ def kb_select(bot, update, groups): send_async(bot, chat.id, text=_("Enabled statistics!")) elif option == Emoji.EARTH_GLOBE_EUROPE_AFRICA: + kb = [[locale + ' - ' + descr] + for locale, descr + in sorted(available_locales.items())] send_async(bot, chat.id, text=_("Select locale"), - reply_markup=ReplyKeyboardMarkup(keyboard=available_locales, + reply_markup=ReplyKeyboardMarkup(keyboard=kb, one_time_keyboard=True)) elif option == Emoji.CROSS_MARK: @@ -102,9 +88,7 @@ def locale_select(bot, update, groups): user = update.message.from_user option = groups[0] - if option in [locale.split()[0] - for row in available_locales - for locale in row]: + if option in available_locales: us = UserSetting.get(id=user.id) us.lang = option _.push(option) @@ -112,10 +96,11 @@ def locale_select(bot, update, groups): _.pop() -dispatcher.add_handler(CommandHandler('settings', show_settings)) -dispatcher.add_handler(RegexHandler('^([' + Emoji.BAR_CHART + - Emoji.EARTH_GLOBE_EUROPE_AFRICA + - Emoji.CROSS_MARK + ']) .+$', - kb_select, pass_groups=True)) -dispatcher.add_handler(RegexHandler(r'^(\w\w_\w\w) - .*', - locale_select, pass_groups=True)) +def register(): + dispatcher.add_handler(CommandHandler('settings', show_settings)) + dispatcher.add_handler(RegexHandler('^([' + Emoji.BAR_CHART + + Emoji.EARTH_GLOBE_EUROPE_AFRICA + + Emoji.CROSS_MARK + ']) .+$', + kb_select, pass_groups=True)) + dispatcher.add_handler(RegexHandler(r'^(\w\w_\w\w) - .*', + locale_select, pass_groups=True)) diff --git a/simple_commands.py b/simple_commands.py index 5b1501e..1c7d059 100644 --- a/simple_commands.py +++ b/simple_commands.py @@ -21,8 +21,9 @@ from telegram import ParseMode from telegram.ext import CommandHandler from user_setting import UserSetting -from utils import _, send_async, user_locale +from utils import send_async from shared_vars import dispatcher +from internationalization import _, user_locale help_text = ("Follow these steps:\n\n" "1. Add this bot to a group\n" @@ -101,18 +102,34 @@ def stats(bot, update): "a private chat with the bot to enable them.")) else: stats_text = list() + + n = us.games_played stats_text.append( - _("{number} games played").format(number=us.games_played)) + _("{number} game played", + "{number} games played", + n).format(number=n) + ) + + n = us.first_places stats_text.append( - _("{number} first places").format(number=us.first_places)) + _("{number} first places", + "{number} first places", + n).format(number=n) + ) + + n = us.cards_played stats_text.append( - _("{number} cards played").format(number=us.cards_played)) + _("{number} card played", + "{number} cards played", + n).format(number=n) + ) send_async(bot, update.message.chat_id, text='\n'.join(stats_text)) -dispatcher.add_handler(CommandHandler('help', help)) -dispatcher.add_handler(CommandHandler('source', source)) -dispatcher.add_handler(CommandHandler('news', news)) -dispatcher.add_handler(CommandHandler('stats', stats)) +def register(): + dispatcher.add_handler(CommandHandler('help', help)) + dispatcher.add_handler(CommandHandler('source', source)) + dispatcher.add_handler(CommandHandler('news', news)) + dispatcher.add_handler(CommandHandler('stats', stats)) diff --git a/utils.py b/utils.py index fdde8aa..134a1b0 100644 --- a/utils.py +++ b/utils.py @@ -19,52 +19,17 @@ import logging -from functools import wraps - -from flufl.i18n import registry -from flufl.i18n import PackageStrategy from telegram import Emoji from telegram.ext.dispatcher import run_async -import locales -from database import db_session -from user_setting import UserSetting -from shared_vars import gm -strategy = PackageStrategy('unobot', locales) -application = registry.register(strategy) -_ = application._ +from internationalization import _, __ + logger = logging.getLogger(__name__) TIMEOUT = 2.5 -def __(string, multi_translate): - """Translates text into all locales on the stack""" - translations = list() - locales = list() - - if not multi_translate: - _.push('en_US') - translations.append(_(string)) - _.pop() - - else: - while _.code: - translation = _(string) - - if translation not in translations: - translations.append(translation) - - locales.append(_.code) - _.pop() - - for l in reversed(locales): - _.push(l) - - return '\n'.join(translations) - - def list_subtract(list1, list2): """ Helper function to subtract two lists and return the sorted result """ list1 = list1.copy() @@ -138,76 +103,3 @@ def answer_async(bot, *args, **kwargs): bot.answerInlineQuery(*args, **kwargs) except Exception as e: error(None, None, e) - - -def user_locale(func): - @wraps(func) - @db_session - def wrapped(bot, update, *pargs, **kwargs): - user, chat = _user_chat_from_update(update) - - with db_session: - us = UserSetting.get(id=user.id) - - if us: - _.push(us.lang) - else: - _.push('en_US') - - result = func(bot, update, *pargs, **kwargs) - _.pop() - return result - return wrapped - - -def game_locales(func): - @wraps(func) - @db_session - def wrapped(bot, update, *pargs, **kwargs): - user, chat = _user_chat_from_update(update) - player = gm.player_for_user_in_chat(user, chat) - locales = list() - - if player: - for player in player.game.players: - us = UserSetting.get(id=player.user.id) - - if us: - loc = us.lang - else: - loc = 'en_US' - - if loc in locales: - continue - - _.push(loc) - locales.append(loc) - - result = func(bot, update, *pargs, **kwargs) - - while _.code: - _.pop() - - return result - return wrapped - - -def _user_chat_from_update(update): - - try: - user = update.message.from_user - chat = update.message.chat - except (NameError, AttributeError): - try: - user = update.inline_query.from_user - chat = gm.userid_current[user.id].game.chat - except KeyError: - chat = None - except (NameError, AttributeError): - try: - user = update.chosen_inline_result.from_user - chat = gm.userid_current[user.id].game.chat - except (NameError, AttributeError): - chat = None - - return user, chat