From d5f1f71a0a7a3940a4973c7adf0cad1ad99432b1 Mon Sep 17 00:00:00 2001 From: Romain J Date: Sun, 29 Sep 2019 23:01:49 +0200 Subject: [PATCH] add(i18n): create a language switcher command for all guilds --- cogs/admin.py | 94 +++++++++++++------------ cogs/basics.py | 24 +++---- cogs/utility.py | 18 ++--- cogs/utils/lang.py | 31 +++++++- cogs/utils/models/lang.py | 13 ++++ extras/locales/en/LC_MESSAGES/admin.po | 8 ++- extras/locales/fr/LC_MESSAGES/admin.mo | Bin 1144 -> 1299 bytes extras/locales/fr/LC_MESSAGES/admin.po | 8 ++- 8 files changed, 125 insertions(+), 71 deletions(-) create mode 100644 cogs/utils/models/lang.py diff --git a/cogs/admin.py b/cogs/admin.py index a211a9a..c637203 100644 --- a/cogs/admin.py +++ b/cogs/admin.py @@ -9,6 +9,7 @@ import humanize from discord.ext import commands from bot import TuxBot +from .utils.models.lang import Lang from .utils.lang import Texts from .utils.models.warn import Warn @@ -35,7 +36,7 @@ class Admin(commands.Cog): member: discord.Member = kwargs.get('member') reason = kwargs.get( 'reason', - Texts('admin').get("Please enter a reason") + Texts('admin', ctx).get("Please enter a reason") ) if kwargs.get('type') == 'ban': @@ -86,7 +87,7 @@ class Admin(commands.Cog): message_id) await message.edit(content=content) except (discord.errors.NotFound, discord.errors.Forbidden): - await ctx.send(Texts('utils').get("Unable to find the message"), + await ctx.send(Texts('utils', ctx).get("Unable to find the message"), delete_after=5) @_say.command(name='to') @@ -119,10 +120,10 @@ class Admin(commands.Cog): await ctx.send(embed=e) except discord.Forbidden: - await ctx.send(Texts('admin').get("Unable to ban this user"), + await ctx.send(Texts('admin', ctx).get("Unable to ban this user"), delete_after=5) except discord.errors.NotFound: - await ctx.send(Texts('utils').get("Unable to find the user..."), + await ctx.send(Texts('utils', ctx).get("Unable to find the user..."), delete_after=5) """---------------------------------------------------------------------""" @@ -144,10 +145,10 @@ class Admin(commands.Cog): await ctx.send(embed=e) except discord.Forbidden: - await ctx.send(Texts('admin').get("Unable to kick this user"), + await ctx.send(Texts('admin', ctx).get("Unable to kick this user"), delete_after=5) except discord.errors.NotFound: - await ctx.send(Texts('utils').get("Unable to find the user..."), + await ctx.send(Texts('utils', ctx).get("Unable to find the user..."), delete_after=5) """---------------------------------------------------------------------""" @@ -179,7 +180,7 @@ class Admin(commands.Cog): for emoji in emojis: await message.add_reaction(emoji) except discord.errors.NotFound: - await ctx.send(Texts('utils').get("Unable to find the message"), + await ctx.send(Texts('utils', ctx).get("Unable to find the message"), delete_after=5) @_react.command(name='clear') @@ -189,7 +190,7 @@ class Admin(commands.Cog): message_id) await message.clear_reactions() except discord.errors.NotFound: - await ctx.send(Texts('utils').get("Unable to find the message"), + await ctx.send(Texts('utils', ctx).get("Unable to find the message"), delete_after=5) """---------------------------------------------------------------------""" @@ -206,7 +207,7 @@ class Admin(commands.Cog): message_id) await message.delete() except (discord.errors.NotFound, discord.errors.Forbidden): - await ctx.send(Texts('utils').get("Unable to find the message"), + await ctx.send(Texts('utils', ctx).get("Unable to find the message"), delete_after=5) @_delete.command(name='from', aliases=['to', 'in']) @@ -222,7 +223,7 @@ class Admin(commands.Cog): message_id) await message.delete() except (discord.errors.NotFound, discord.errors.Forbidden): - await ctx.send(Texts('utils').get("Unable to find the message"), + await ctx.send(Texts('utils', ctx).get("Unable to find the message"), delete_after=5) """---------------------------------------------------------------------""" @@ -276,7 +277,7 @@ class Admin(commands.Cog): if ctx.invoked_subcommand is None: warns_list, warns = await self.get_warn(ctx) e = discord.Embed( - title=f"{warns.count()} {Texts('admin').get('last warns')}: ", + title=f"{warns.count()} {Texts('admin', ctx).get('last warns')}: ", description=warns_list ) @@ -288,7 +289,7 @@ class Admin(commands.Cog): member = await ctx.guild.fetch_member(member.id) if not member: return await ctx.send( - Texts('utils').get("Unable to find the user...") + Texts('utils', ctx).get("Unable to find the user...") ) def check(pld: discord.RawReactionActionEvent): @@ -301,15 +302,15 @@ class Admin(commands.Cog): if warns.count() >= 3: e = discord.Embed( - title=Texts('admin').get('More than 2 warns'), + title=Texts('admin', ctx).get('More than 2 warns'), description=f"{member.mention} " - + Texts('admin').get('has more than 2 warns') + + Texts('admin', ctx).get('has more than 2 warns') ) e.add_field( name='__Actions__', value=':one: kick\n' ':two: ban\n' - ':three: ' + Texts('admin').get('ignore') + ':three: ' + Texts('admin', ctx).get('ignore') ) choice = await ctx.send(embed=e) @@ -325,7 +326,7 @@ class Admin(commands.Cog): ) except asyncio.TimeoutError: return await ctx.send( - Texts('admin').get('Took too long. Aborting.') + Texts('admin', ctx).get('Took too long. Aborting.') ) finally: await choice.delete() @@ -338,7 +339,7 @@ class Admin(commands.Cog): content=f"{ctx.prefix}" f"kick " f"{member} " - f"{Texts('admin').get('More than 2 warns')}" + f"{Texts('admin', ctx).get('More than 2 warns')}" ) return await alt_ctx.command.invoke(alt_ctx) @@ -350,15 +351,15 @@ class Admin(commands.Cog): content=f"{ctx.prefix}" f"ban " f"{member} " - f"{Texts('admin').get('More than 2 warns')}" + f"{Texts('admin', ctx).get('More than 2 warns')}" ) return await alt_ctx.command.invoke(alt_ctx) await self.add_warn(ctx, member, reason) await ctx.send( content=f"{member.mention} " - f"**{Texts('admin').get('got a warn')}**" - f"\n**{Texts('admin').get('Reason')}:** `{reason}`" + f"**{Texts('admin', ctx).get('got a warn')}**" + f"\n**{Texts('admin', ctx).get('Reason')}:** `{reason}`" ) @_warn.command(name='remove', aliases=['revoke']) @@ -366,15 +367,15 @@ class Admin(commands.Cog): warn = self.bot.engine.query(Warn).filter(Warn.id == warn_id).one() self.bot.engine.delete(warn) - await ctx.send(f"{Texts('admin').get('Warn with id')} `{warn_id}`" - f" {Texts('admin').get('successfully removed')}") + await ctx.send(f"{Texts('admin', ctx).get('Warn with id')} `{warn_id}`" + f" {Texts('admin', ctx).get('successfully removed')}") @_warn.command(name='show', aliases=['list']) async def _warn_show(self, ctx: commands.Context, member: discord.Member): warns_list, warns = await self.get_warn(ctx, member) e = discord.Embed( - title=f"{warns.count()} {Texts('admin').get('last warns')}: ", + title=f"{warns.count()} {Texts('admin', ctx).get('last warns')}: ", description=warns_list ) @@ -386,35 +387,36 @@ class Admin(commands.Cog): warn.reason = reason self.bot.engine.commit() - await ctx.send(f"{Texts('admin').get('Warn with id')} `{warn_id}`" - f" {Texts('admin').get('successfully edited')}") + await ctx.send(f"{Texts('admin', ctx).get('Warn with id')} `{warn_id}`" + f" {Texts('admin', ctx).get('successfully edited')}") """---------------------------------------------------------------------""" @commands.command(name='language', aliases=['lang', 'langue', 'langage']) - async def _language(self, ctx: commands.Context, locale): - query = """ - SELECT locale - FROM lang - WHERE key = 'available' - """ + async def _language(self, ctx: commands.Context, locale: str): + available = self.bot.engine\ + .query(Lang.value)\ + .filter(Lang.key == 'available')\ + .one()[0]\ + .split(', ') - async with self.bot.engine.begin() as con: - await ctx.trigger_typing() - available = list(await con.fetchrow(query)) + if locale.lower() not in available: + await ctx.send(Texts('admin', ctx).get('Unable to find this language')) + else: + current = self.bot.engine\ + .query(Lang)\ + .filter(Lang.key == str(ctx.guild.id)) - if str(locale) in available: - query = """ - IF EXISTS(SELECT * FROM lang WHERE key = $1 ) - then - UPDATE lang - SET locale = $2 - WHERE key = $1 - ELSE - INSERT INTO lang (key, locale) - VALUES ($1, $2) - """ - await con.fetch(query, str(ctx.guild.id), str(locale)) + if current.count() > 0: + current = current.one() + current.value = locale.lower() + self.bot.engine.commit() + else: + new_row = Lang(key=str(ctx.guild.id), value=locale.lower()) + self.bot.engine.add(new_row) + self.bot.engine.commit() + + await ctx.send(Texts('admin', ctx).get('Language changed successfully')) def setup(bot: TuxBot): diff --git a/cogs/basics.py b/cogs/basics.py index ff41266..ce1263b 100644 --- a/cogs/basics.py +++ b/cogs/basics.py @@ -68,17 +68,17 @@ class Basics(commands.Cog): with proc.oneshot(): mem = proc.memory_full_info() e = discord.Embed( - title=f"{Texts('basics').get('Information about TuxBot')}", + title=f"{Texts('basics', ctx).get('Information about TuxBot')}", color=0x89C4F9) e.add_field( - name=f"__{Texts('basics').get('Latest changes')}__", + name=f"__{Texts('basics', ctx).get('Latest changes')}__", value=self._latest_commits(), inline=False) e.add_field( name=f"__:busts_in_silhouette: " - f"{Texts('basics').get('Development')}__", + f"{Texts('basics', ctx).get('Development')}__", value=f"**Romain#5117:** [git](https://git.gnous.eu/Romain)\n" f"**Outout#4039:** [git](https://git.gnous.eu/mael)\n", inline=True @@ -92,41 +92,41 @@ class Basics(commands.Cog): e.add_field( name="__:gear: Usage__", value=f"**{humanize.naturalsize(mem.rss)}** " - f"{Texts('basics').get('physical memory')}\n" + f"{Texts('basics', ctx).get('physical memory')}\n" f"**{humanize.naturalsize(mem.vms)}** " - f"{Texts('basics').get('virtual memory')}\n", + f"{Texts('basics', ctx).get('virtual memory')}\n", inline=True ) e.add_field( - name=f"__{Texts('basics').get('Servers count')}__", + name=f"__{Texts('basics', ctx).get('Servers count')}__", value=str(len(self.bot.guilds)), inline=True ) e.add_field( - name=f"__{Texts('basics').get('Channels count')}__", + name=f"__{Texts('basics', ctx).get('Channels count')}__", value=str(len([_ for _ in self.bot.get_all_channels()])), inline=True ) e.add_field( - name=f"__{Texts('basics').get('Members count')}__", + name=f"__{Texts('basics', ctx).get('Members count')}__", value=str(len([_ for _ in self.bot.get_all_members()])), inline=True ) e.add_field( - name=f"__:file_folder: {Texts('basics').get('Files')}__", + name=f"__:file_folder: {Texts('basics', ctx).get('Files')}__", value=str(files), inline=True ) e.add_field( - name=f"__¶ {Texts('basics').get('Lines')}__", + name=f"__¶ {Texts('basics', ctx).get('Lines')}__", value=str(lines), inline=True ) e.add_field( - name=f"__:link: {Texts('basics').get('Links')}__", + name=f"__:link: {Texts('basics', ctx).get('Links')}__", value="[tuxbot.gnous.eu](https://tuxbot.gnous.eu/) " "| [gnous.eu](https://gnous.eu/) " f"| [{Texts('basics').get('Invite')}](https://discordapp.com/oauth2/authorize?client_id=301062143942590465&scope=bot&permissions=268749888)", @@ -142,7 +142,7 @@ class Basics(commands.Cog): @commands.command(name='credits', aliases=['contributors', 'authors']) async def _credits(self, ctx: commands.Context): e = discord.Embed( - title=Texts('basics').get('Contributors'), + title=Texts('basics', ctx).get('Contributors'), color=0x36393f ) diff --git a/cogs/utility.py b/cogs/utility.py index 382ecda..03bcc65 100644 --- a/cogs/utility.py +++ b/cogs/utility.py @@ -28,7 +28,7 @@ class Utility(commands.Cog): ip = socket.getaddrinfo(addr, None, socket.AF_INET6)[1][4][0] except socket.gaierror: return await ctx.send( - Texts('utility').get('ipv6 not available')) + Texts('utility', ctx).get('ipv6 not available')) else: ip = socket.gethostbyname(addr) @@ -37,19 +37,19 @@ class Utility(commands.Cog): if response.get('status') == 'success': e = discord.Embed( - title=f"{Texts('utility').get('Information for')} " + title=f"{Texts('utility', ctx).get('Information for')} " f"``{addr}`` *`({response.get('query')})`*", color=0x5858d7 ) e.add_field( - name=Texts('utility').get('Belongs to :'), + name=Texts('utility', ctx).get('Belongs to :'), value=response.get('org', 'N/A'), inline=False ) e.add_field( - name=Texts('utility').get('Is located at :'), + name=Texts('utility', ctx).get('Is located at :'), value=response.get('city', 'N/A'), inline=True ) @@ -68,7 +68,7 @@ class Utility(commands.Cog): await ctx.send(embed=e) else: await ctx.send( - content=f"{Texts('utility').get('info not available')}" + content=f"{Texts('utility', ctx).get('info not available')}" f"``{response.get('query')}``") """---------------------------------------------------------------------""" @@ -82,7 +82,7 @@ class Utility(commands.Cog): async with self.bot.session.get(addr) as s: await ctx.trigger_typing() e = discord.Embed( - title=f"{Texts('utility').get('Headers of')} {addr}", + title=f"{Texts('utility', ctx).get('Headers of')} {addr}", color=0xd75858 ) e.add_field(name="Status", value=s.status, inline=True) @@ -96,7 +96,7 @@ class Utility(commands.Cog): await ctx.send(embed=e) except aiohttp.client_exceptions.ClientConnectorError: - await ctx.send(f"{Texts('utility').get('Cannot connect to host')} " + await ctx.send(f"{Texts('utility', ctx).get('Cannot connect to host')} " f"{addr}") """---------------------------------------------------------------------""" @@ -104,8 +104,8 @@ class Utility(commands.Cog): @commands.command(name='git', aliases=['sources', 'source', 'github']) async def _git(self, ctx): e = discord.Embed( - title=Texts('utility').get('git repo'), - description=Texts('utility').get('git text'), + title=Texts('utility', ctx).get('git repo'), + description=Texts('utility', ctx).get('git text'), colour=0xE9D460 ) e.set_author( diff --git a/cogs/utils/lang.py b/cogs/utils/lang.py index 2978056..48b69ca 100644 --- a/cogs/utils/lang.py +++ b/cogs/utils/lang.py @@ -1,10 +1,15 @@ import gettext import config +from .models.lang import Lang +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker +from discord.ext import commands + class Texts: - def __init__(self, base: str = 'base'): - self.locale = config.locale + def __init__(self, base: str = 'base', ctx: commands.Context = None): + self.locale = self.get_locale(ctx) self.base = base def get(self, text: str) -> str: @@ -15,3 +20,25 @@ class Texts: def set(self, lang: str): self.locale = lang + + @staticmethod + def get_locale(ctx): + engine = create_engine(config.postgresql) + + Session = sessionmaker() + Session.configure(bind=engine) + + session = Session() + + if ctx is not None: + current = session\ + .query(Lang.value)\ + .filter(Lang.key == str(ctx.guild.id)) + if current.count() > 0: + return current.one()[0] + + default = session\ + .query(Lang.value)\ + .filter(Lang.key == 'default')\ + .one()[0] + return default diff --git a/cogs/utils/models/lang.py b/cogs/utils/models/lang.py new file mode 100644 index 0000000..bec56be --- /dev/null +++ b/cogs/utils/models/lang.py @@ -0,0 +1,13 @@ +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy import Column, String +Base = declarative_base() + + +class Lang(Base): + __tablename__ = 'lang' + + key = Column(String, primary_key=True) + value = Column(String) + + def __repr__(self): + return "" % (self.key, self.value) diff --git a/extras/locales/en/LC_MESSAGES/admin.po b/extras/locales/en/LC_MESSAGES/admin.po index 3bec23b..69320d1 100644 --- a/extras/locales/en/LC_MESSAGES/admin.po +++ b/extras/locales/en/LC_MESSAGES/admin.po @@ -52,4 +52,10 @@ msgid "successfully removed" msgstr "" msgid "successfully edited" -msgstr "" \ No newline at end of file +msgstr "" + +msgid "Unable to find this language" +msgstr "" + +msgid "Language changed successfully" +msgstr "" diff --git a/extras/locales/fr/LC_MESSAGES/admin.mo b/extras/locales/fr/LC_MESSAGES/admin.mo index f08390cf07ba5261daf655473466283fca31f7e5..cb6ea86b4d2b9b1b8667b9b8011ff08751d4cba5 100644 GIT binary patch delta 414 zcmYk$y-Ncz7{~FPSE>{RTVJZ!N)X*eaPj3oP;hc`Inwk+k1EYw(W!%rtDK$m575QM zMR1Z^+{9VvhQne*hzA#>I7jg6{6eu6G;48C(^A#> z(iLf9rL-~CI1EqDe+~-OMzbQLdc(-jUA^1$9=-W~qiaiVv<%u>^_$acBX*KcznHB0 X;k1@f>o9KXmQ*^5^k9fiF8sGYe85O* delta 277 zcmYk$uL=S|5XbRZJX8)*{D+DP1`iA-gXsi|QLtI`0c@7n?qT z-Jsxi@PZ3JK0C8J%Ubu94(