implement plurals, update de_DE translation to use plurals

This commit is contained in:
Jannes Höke 2016-05-24 15:49:23 +02:00
parent c54ffd83e5
commit 6901e7f4d0
10 changed files with 410 additions and 233 deletions

58
bot.py
View file

@ -37,11 +37,12 @@ from utils import display_name
import card as c import card as c
from errors import (NoGameInChatError, LobbyClosedError, AlreadyJoinedError, from errors import (NoGameInChatError, LobbyClosedError, AlreadyJoinedError,
NotEnoughPlayersError, DeckEmptyError) NotEnoughPlayersError, DeckEmptyError)
from utils import _, __, send_async, answer_async, user_locale, game_locales, \ from utils import send_async, answer_async, error, TIMEOUT
error, TIMEOUT
from shared_vars import botan, gm, updater, dispatcher 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 from simple_commands import help
@ -136,11 +137,12 @@ def leave_game(bot, update):
except NotEnoughPlayersError: except NotEnoughPlayersError:
gm.end_game(chat, user) 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: else:
send_async(bot, chat.id, 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)), name=display_name(game.current_player.user)),
reply_to_message_id=update.message.message_id) reply_to_message_id=update.message.message_id)
@ -197,10 +199,11 @@ def status_update(bot, update):
pass pass
except NotEnoughPlayersError: except NotEnoughPlayersError:
gm.end_game(chat, user) 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: else:
send_async(bot, chat.id, text=__("Removing {name} from the game", send_async(bot, chat.id, text=__("Removing {name} from the game",
game.translate) multi=game.translate)
.format(name=display_name(user))) .format(name=display_name(user)))
@ -236,7 +239,7 @@ def start_game(bot, update, args):
__("First player: {name}\n" __("First player: {name}\n"
"Use /close to stop people from joining the game.\n" "Use /close to stop people from joining the game.\n"
"Enable multi-translations with /enable_translations", "Enable multi-translations with /enable_translations",
game.translate) multi=game.translate)
.format(name=display_name(game.current_player.user))) .format(name=display_name(game.current_player.user)))
@run_async @run_async
@ -410,9 +413,12 @@ def skip_player(bot, update):
delta = (now - started).seconds delta = (now - started).seconds
if delta < skipped_player.waiting_time: if delta < skipped_player.waiting_time:
n = skipped_player.waiting_time - delta
send_async(bot, chat.id, send_async(bot, chat.id,
text=_("Please wait {time} seconds") text=_("Please wait {time} second",
.format(time=(skipped_player.waiting_time - delta)), "Please wait {time} seconds",
n)
.format(time=n),
reply_to_message_id=update.message.message_id) reply_to_message_id=update.message.message_id)
elif skipped_player.waiting_time > 0: elif skipped_player.waiting_time > 0:
@ -423,11 +429,17 @@ def skip_player(bot, update):
except DeckEmptyError: except DeckEmptyError:
pass pass
n = skipped_player.waiting_time
send_async(bot, chat.id, send_async(bot, chat.id,
text=__("Waiting time to skip this player has " 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" "been reduced to {time} seconds.\n"
"Next player: {name}", game.translate) "Next player: {name}",
.format(time=skipped_player.waiting_time, n,
multi=game.translate)
.format(time=n,
name=display_name(next_player.user))) name=display_name(next_player.user)))
game.turn() game.turn()
@ -437,7 +449,7 @@ def skip_player(bot, update):
send_async(bot, chat.id, send_async(bot, chat.id,
text=__("{name1} was skipped four times in a row " text=__("{name1} was skipped four times in a row "
"and has been removed from the game.\n" "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), .format(name1=display_name(skipped_player.user),
name2=display_name(next_player.user))) name2=display_name(next_player.user)))
@ -445,7 +457,7 @@ def skip_player(bot, update):
send_async(bot, chat.id, send_async(bot, chat.id,
text=__("{name} was skipped four times in a row " text=__("{name} was skipped four times in a row "
"and has been removed from the game.\n" "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))) .format(name=display_name(skipped_player.user)))
gm.end_game(chat.id, 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. Builds the result list for inline queries and answers to the client.
""" """
results = list() results = list()
playable = list()
switch = None switch = None
try: try:
@ -476,7 +487,7 @@ def reply_to_query(bot, update):
elif user_id == game.current_player.user.id: elif user_id == game.current_player.user.id:
if game.choosing_color: if game.choosing_color:
add_choose_color(results, game) add_choose_color(results, game)
add_other_cards(playable, player, results, game) add_other_cards(player, results, game)
else: else:
if not player.drew: if not player.drew:
add_draw(player, results) add_draw(player, results)
@ -541,7 +552,7 @@ def process_result(bot, update):
return return
elif int(anti_cheat) != last_anti_cheat: elif int(anti_cheat) != last_anti_cheat:
send_async(bot, chat.id, 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))) .format(name=display_name(player.user)))
return return
elif result_id == 'call_bluff': elif result_id == 'call_bluff':
@ -560,7 +571,7 @@ def process_result(bot, update):
if game in gm.chatid_games.get(chat.id, list()): if game in gm.chatid_games.get(chat.id, list()):
send_async(bot, chat.id, 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))) .format(name=display_name(game.current_player.user)))
@ -572,7 +583,7 @@ def reset_waiting_time(bot, player):
player.waiting_time = 90 player.waiting_time = 90
send_async(bot, chat.id, send_async(bot, chat.id,
text=__("Waiting time for {name} has been reset to 90 " 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))) .format(name=display_name(player.user)))
@ -598,7 +609,7 @@ def do_play_card(bot, player, result_id):
if len(player.cards) == 0: if len(player.cards) == 0:
send_async(bot, chat.id, send_async(bot, chat.id,
text=__("{name} won!", game.translate) text=__("{name} won!", multi=game.translate)
.format(name=user.first_name)) .format(name=user.first_name))
if us.stats: if us.stats:
@ -612,7 +623,8 @@ def do_play_card(bot, player, result_id):
try: try:
gm.leave_game(user, chat) gm.leave_game(user, chat)
except NotEnoughPlayersError: 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) us2 = UserSetting.get(id=game.current_player.user.id)
if us2 and us2.stats: if us2 and us2.stats:
@ -636,7 +648,7 @@ def do_draw(bot, player):
except DeckEmptyError: except DeckEmptyError:
send_async(bot, player.game.chat.id, send_async(bot, player.game.chat.id,
text=__("There are no more cards in the deck.", text=__("There are no more cards in the deck.",
game.translate)) multi=game.translate))
if (game.last_card.value == c.DRAW_TWO or if (game.last_card.value == c.DRAW_TWO or
game.last_card.special == c.DRAW_FOUR) and \ game.last_card.special == c.DRAW_FOUR) and \
@ -694,6 +706,8 @@ dispatcher.add_handler(CommandHandler('enable_translations',
dispatcher.add_handler(CommandHandler('disable_translations', dispatcher.add_handler(CommandHandler('disable_translations',
disable_translations)) disable_translations))
dispatcher.add_handler(CommandHandler('skip', skip_player)) dispatcher.add_handler(CommandHandler('skip', skip_player))
simple_commands.register()
settings.register()
dispatcher.add_handler(MessageHandler([Filters.status_update], status_update)) dispatcher.add_handler(MessageHandler([Filters.status_update], status_update))
dispatcher.add_error_handler(error) dispatcher.add_error_handler(error)

175
internationalization.py Normal file
View file

@ -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 <uno@jhoeke.de>
#
# 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 <http://www.gnu.org/licenses/>.
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

View file

@ -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 <uno@jhoeke.de>
#
# 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 <http://www.gnu.org/licenses/>.
from .available import available_locales

37
locales/available.py Normal file
View file

@ -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 <uno@jhoeke.de>
#
# 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 <http://www.gnu.org/licenses/>.
"""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') + ' 中文(香港)'}

View file

@ -29,7 +29,7 @@ msgstr ""
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n" "Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 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" "X-Generator: Gtranslator 2.91.6\n"
#: bot.py:60 #: bot.py:60
@ -229,17 +229,22 @@ msgstr "Du spielst kein Spiel in dieser Gruppe."
#: bot.py:400 #: bot.py:400
#, python-format #, python-format
msgid "Please wait {time} seconds" msgid "Please wait {time} second"
msgstr "Bitte warte {time} Sekunden" msgid_plural "Please wait {time} seconds"
msgstr[0] "Bitte warte {time} Sekunde"
msgstr[1] "Bitte warte {time} Sekunden"
#: bot.py:413 #: bot.py:413
#, python-format #, python-format
msgid "" msgid "Waiting time to skip this player has been reduced to {time} second.\n"
"Waiting time to skip this player has been reduced to {time} seconds.\n"
"Next player: {name}" "Next player: {name}"
msgstr "" msgid_plural "Waiting time to skip this player has been reduced to {time} seconds.\n"
"Die Wartezeit um diesen Spieler zu überspringen wurde auf {time} Sekunden " "Next player: {name}"
"reduziert.\n" 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}" "Nächster Spieler: {name}"
#: bot.py:424 #: bot.py:424
@ -313,8 +318,10 @@ msgid "Choose Color"
msgstr "Wähle Farbe" msgstr "Wähle Farbe"
#: results.py:56 #: results.py:56
msgid "Cards (tap for game state):" msgid "Last card (tap for game state):"
msgstr "Karten (tippe für Spielinfo):" 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 #: results.py:60 results.py:123 results.py:165
msgid "Current player: {name}" msgid "Current player: {name}"
@ -325,13 +332,17 @@ msgid "Last card: {card}"
msgstr "Letzte Karte: {card}" msgstr "Letzte Karte: {card}"
#: results.py:62 results.py:125 results.py:168 #: results.py:62 results.py:125 results.py:168
msgid "Players: {player_list}" msgid "Player: {player_list}"
msgstr "Spieler: {player_list}" msgid_plural "Players: {player_list}"
msgstr[0] "Spieler: {player_list}"
msgstr[1] "Spieler: {player_list}"
#: results.py:72 #: results.py:72
#, python-format #, python-format
msgid "{name} ({number} cards)" msgid "{name} ({number} card)"
msgstr "{name} ({number} Karten)" msgid_plural "{name} ({number} cards)"
msgstr[0] "{name} ({number} Karte)"
msgstr[1] "{name} ({number} Karten)"
#: results.py:81 #: results.py:81
msgid "You are not playing" msgid "You are not playing"
@ -355,11 +366,10 @@ msgstr "Starte das Spiel mit /start"
#: results.py:108 #: results.py:108
#, python-format #, python-format
msgid "Drawing 1 card" msgid "Drawing {number} card"
msgstr "Zieht 1 Karte" msgid_plural "Drawing {number} cards"
msgstr[0] "Ich ziehe {number} Karte"
msgid "Drawing {number} cards" msgstr[1] "Ich ziehe {number} Karten"
msgstr "Zieht {number} Karten"
#: results.py:136 #: results.py:136
msgid "Pass" 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." "/settings-Kommando in einem privaten Chat mit dem Bot."
#: simple_commands.py #: simple_commands.py
msgid "{number} games played" msgid "{number} game played"
msgstr "{number} gespielte Spiele" msgid_plural "{number} games played"
msgstr[0] "{number} gespieltes Spiel"
msgstr[1] "{number} gespielte Spiele"
#: simple_commands.py #: simple_commands.py
msgid "{number} first places" msgid "{number} first place"
msgstr "{number}x 1. Platz" msgid_plural "{number} first places"
msgstr[0] "{number} mal 1. Platz"
msgstr[1] "{number} mal 1. Platz"
#: simple_commands.py #: simple_commands.py
msgid "{number} cards played" msgid "{number} card played"
msgstr "{number} gespielte Karten" msgid_plural "{number} cards played"
msgstr[0] "{number} gespielte Karte"
msgstr[1] "{number} gespielte Karten"
#: utils.py #: utils.py

View file

@ -29,6 +29,7 @@ msgstr ""
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n" "Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: utf-8\n" "Content-Transfer-Encoding: utf-8\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: bot.py:60 #: bot.py:60
@ -184,14 +185,19 @@ msgstr ""
#: bot.py:400 #: bot.py:400
#, python-format #, python-format
msgid "Please wait {time} seconds" msgid "Please wait {time} second"
msgstr "" msgid_plural "Please wait {time} seconds"
msgstr[0] ""
msgstr[1] ""
#: bot.py:413 #: bot.py:413
#, python-format #, 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}" "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 #: bot.py:424
#, python-format #, python-format
@ -256,8 +262,10 @@ msgid "Choose Color"
msgstr "" msgstr ""
#: results.py:56 #: results.py:56
msgid "Cards (tap for game state):" msgid "Last card (tap for game state):"
msgstr "" msgid_plural "Cards (tap for game state):"
msgstr[0] ""
msgstr[1] ""
#: results.py:60 results.py:123 results.py:165 #: results.py:60 results.py:123 results.py:165
msgid "Current player: {name}" msgid "Current player: {name}"
@ -268,13 +276,17 @@ msgid "Last card: {card}"
msgstr "" msgstr ""
#: results.py:62 results.py:125 results.py:168 #: results.py:62 results.py:125 results.py:168
msgid "Players: {player_list}" msgid "Player: {player_list}"
msgstr "" msgid_plural "Players: {player_list}"
msgstr[0] ""
msgstr[1] ""
#: results.py:72 #: results.py:72
#, python-format #, python-format
msgid "{name} ({number} cards)" msgid "{name} ({number} card)"
msgstr "" msgid_plural "{name} ({number} cards)"
msgstr[0] ""
msgstr[1] ""
#: results.py:81 #: results.py:81
msgid "You are not playing" msgid "You are not playing"
@ -295,11 +307,10 @@ msgstr ""
#: results.py:108 #: results.py:108
#, python-format #, python-format
msgid "Drawing 1 card" msgid "Drawing {number} card"
msgstr "" msgid_plural "Drawing {number} cards"
msgstr[0] ""
msgid "Drawing {number} cards" msgstr[1] ""
msgstr ""
#: results.py:136 #: results.py:136
msgid "Pass" msgid "Pass"
@ -352,16 +363,22 @@ msgid "You did not enable statistics. Use /settings in "
msgstr "" msgstr ""
#: simple_commands.py #: simple_commands.py
msgid "{number} games played" msgid "{number} game played"
msgstr "" msgid_plural "{number} games played"
msgstr[0] ""
msgstr[1] ""
#: simple_commands.py #: simple_commands.py
msgid "{number} first places" msgid "{number} first place"
msgstr "" msgid_plural "{number} first places"
msgstr[0] ""
msgstr[1] ""
#: simple_commands.py #: simple_commands.py
msgid "{number} cards played" msgid "{number} card played"
msgstr "" msgid_plural "{number} cards played"
msgstr[0] ""
msgstr[1] ""
#: utils.py #: utils.py

View file

@ -26,8 +26,8 @@ from telegram import InlineQueryResultArticle, InputTextMessageContent, \
InlineQueryResultCachedSticker as Sticker InlineQueryResultCachedSticker as Sticker
import card as c import card as c
from utils import display_color, display_color_group, display_name, \ from utils import display_color, display_color_group, display_name
list_subtract, _, __ from internationalization import _, __
def add_choose_color(results, game): 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""" """Add hand cards when choosing colors"""
if not playable:
playable = list()
results.append( results.append(
InlineQueryResultArticle( InlineQueryResultArticle(
"hand", "hand",
title=_("Cards (tap for game state):"), title=_("Card (tap for game state):",
description=', '.join([repr(card) for card in "Cards (tap for game state):",
list_subtract(player.cards, playable)]), len(player.cards)),
description=', '.join([repr(card) for card in player.cards]),
input_message_content=game_info(game) input_message_content=game_info(game)
) )
) )
@ -62,7 +61,9 @@ def add_other_cards(playable, player, results, game):
def player_list(game): def player_list(game):
"""Generate list of player strings""" """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)) .format(name=player.user.first_name, number=len(player.cards))
for player in game.players] for player in game.players]
@ -101,10 +102,9 @@ def add_draw(player, results):
Sticker( Sticker(
"draw", sticker_file_id=c.STICKERS['option_draw'], "draw", sticker_file_id=c.STICKERS['option_draw'],
input_message_content= input_message_content=
InputTextMessageContent(__('Drawing 1 card', player.game.translate) InputTextMessageContent(__('Drawing {number} card',
if n == 1 else 'Drawing {number} cards', n,
__('Drawing {number} cards', multi=player.game.translate)
player.game.translate)
.format(number=n)) .format(number=n))
) )
) )
@ -127,8 +127,9 @@ def add_pass(results, game):
results.append( results.append(
Sticker( Sticker(
"pass", sticker_file_id=c.STICKERS['option_pass'], "pass", sticker_file_id=c.STICKERS['option_pass'],
input_message_content=InputTextMessageContent(__('Pass', input_message_content=InputTextMessageContent(
game.translate)) __('Pass', multi=game.translate)
)
) )
) )
@ -141,7 +142,7 @@ def add_call_bluff(results, game):
sticker_file_id=c.STICKERS['option_bluff'], sticker_file_id=c.STICKERS['option_bluff'],
input_message_content= input_message_content=
InputTextMessageContent(__("I'm calling your bluff!", InputTextMessageContent(__("I'm calling your bluff!",
game.translate)) multi=game.translate))
) )
) )
@ -168,6 +169,8 @@ def game_info(game):
"\n" + "\n" +
_("Last card: {card}").format(card=repr(game.last_card)) + _("Last card: {card}").format(card=repr(game.last_card)) +
"\n" + "\n" +
_("Players: {player_list}") _("Player: {player_list}",
"Players: {player_list}",
len(players))
.format(player_list=" -> ".join(players)) .format(player_list=" -> ".join(players))
) )

View file

@ -18,31 +18,14 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import logging
from telegram import ReplyKeyboardMarkup, Emoji from telegram import ReplyKeyboardMarkup, Emoji
from telegram.ext import CommandHandler, RegexHandler from telegram.ext import CommandHandler, RegexHandler
from utils import send_async from utils import send_async
from user_setting import UserSetting from user_setting import UserSetting
from utils import _, user_locale
from shared_vars import dispatcher from shared_vars import dispatcher
from locales import available_locales
OFFSET = 127462 - ord('A') from internationalization import _, user_locale
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') + ' 中文(香港)']]
@user_locale @user_locale
@ -83,8 +66,11 @@ def kb_select(bot, update, groups):
send_async(bot, chat.id, text=_("Enabled statistics!")) send_async(bot, chat.id, text=_("Enabled statistics!"))
elif option == Emoji.EARTH_GLOBE_EUROPE_AFRICA: 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"), send_async(bot, chat.id, text=_("Select locale"),
reply_markup=ReplyKeyboardMarkup(keyboard=available_locales, reply_markup=ReplyKeyboardMarkup(keyboard=kb,
one_time_keyboard=True)) one_time_keyboard=True))
elif option == Emoji.CROSS_MARK: elif option == Emoji.CROSS_MARK:
@ -102,9 +88,7 @@ def locale_select(bot, update, groups):
user = update.message.from_user user = update.message.from_user
option = groups[0] option = groups[0]
if option in [locale.split()[0] if option in available_locales:
for row in available_locales
for locale in row]:
us = UserSetting.get(id=user.id) us = UserSetting.get(id=user.id)
us.lang = option us.lang = option
_.push(option) _.push(option)
@ -112,6 +96,7 @@ def locale_select(bot, update, groups):
_.pop() _.pop()
def register():
dispatcher.add_handler(CommandHandler('settings', show_settings)) dispatcher.add_handler(CommandHandler('settings', show_settings))
dispatcher.add_handler(RegexHandler('^([' + Emoji.BAR_CHART + dispatcher.add_handler(RegexHandler('^([' + Emoji.BAR_CHART +
Emoji.EARTH_GLOBE_EUROPE_AFRICA + Emoji.EARTH_GLOBE_EUROPE_AFRICA +

View file

@ -21,8 +21,9 @@ from telegram import ParseMode
from telegram.ext import CommandHandler from telegram.ext import CommandHandler
from user_setting import UserSetting from user_setting import UserSetting
from utils import _, send_async, user_locale from utils import send_async
from shared_vars import dispatcher from shared_vars import dispatcher
from internationalization import _, user_locale
help_text = ("Follow these steps:\n\n" help_text = ("Follow these steps:\n\n"
"1. Add this bot to a group\n" "1. Add this bot to a group\n"
@ -101,17 +102,33 @@ def stats(bot, update):
"a private chat with the bot to enable them.")) "a private chat with the bot to enable them."))
else: else:
stats_text = list() stats_text = list()
n = us.games_played
stats_text.append( 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( 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( 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, send_async(bot, update.message.chat_id,
text='\n'.join(stats_text)) text='\n'.join(stats_text))
def register():
dispatcher.add_handler(CommandHandler('help', help)) dispatcher.add_handler(CommandHandler('help', help))
dispatcher.add_handler(CommandHandler('source', source)) dispatcher.add_handler(CommandHandler('source', source))
dispatcher.add_handler(CommandHandler('news', news)) dispatcher.add_handler(CommandHandler('news', news))

112
utils.py
View file

@ -19,52 +19,17 @@
import logging import logging
from functools import wraps
from flufl.i18n import registry
from flufl.i18n import PackageStrategy
from telegram import Emoji from telegram import Emoji
from telegram.ext.dispatcher import run_async 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) from internationalization import _, __
application = registry.register(strategy)
_ = application._
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
TIMEOUT = 2.5 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): def list_subtract(list1, list2):
""" Helper function to subtract two lists and return the sorted result """ """ Helper function to subtract two lists and return the sorted result """
list1 = list1.copy() list1 = list1.copy()
@ -138,76 +103,3 @@ def answer_async(bot, *args, **kwargs):
bot.answerInlineQuery(*args, **kwargs) bot.answerInlineQuery(*args, **kwargs)
except Exception as e: except Exception as e:
error(None, None, 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