[Utils] Add humanize_number() function to chat formatting (#2836)

This adds babel as a dependency, and also includes `redbot.core.i18n.get_babel_locale()`
This commit is contained in:
Draper 2019-08-27 23:44:52 +01:00 committed by Toby Harradine
parent 6c3a3fea66
commit 3c1b6ae4cf
15 changed files with 238 additions and 80 deletions

View File

@ -0,0 +1 @@
New :func:`humanize_number` in :module:`redbot.core.utils.chat_formatting` function to convert numbers into text which respect locale.

View File

@ -0,0 +1 @@
New :func:`humanize_number` is used throughout the bot.

View File

@ -19,7 +19,7 @@ import redbot.core
from redbot.core import Config, commands, checks, bank
from redbot.core.data_manager import cog_data_path
from redbot.core.i18n import Translator, cog_i18n
from redbot.core.utils.chat_formatting import bold, box, pagify
from redbot.core.utils.chat_formatting import bold, box, pagify, humanize_number
from redbot.core.utils.menus import (
menu,
DEFAULT_CONTROLS,
@ -442,7 +442,7 @@ class Audio(commands.Cog):
await self._embed_msg(
ctx,
_("Track queueing command price set to {price} {currency}.").format(
price=price, currency=await bank.get_currency_name(ctx.guild)
price=humanize_number(price), currency=await bank.get_currency_name(ctx.guild)
),
)
@ -613,7 +613,9 @@ class Audio(commands.Cog):
msg += _("DJ Role: [{role.name}]\n").format(role=dj_role_obj)
if jukebox:
msg += _("Jukebox: [{jukebox_name}]\n").format(jukebox_name=jukebox)
msg += _("Command price: [{jukebox_price}]\n").format(jukebox_price=jukebox_price)
msg += _("Command price: [{jukebox_price}]\n").format(
jukebox_price=humanize_number(jukebox_price)
)
if maxlength > 0:
msg += _("Max track length: [{tracklength}]\n").format(
tracklength=self._dynamic_time(maxlength)
@ -762,11 +764,15 @@ class Audio(commands.Cog):
em = discord.Embed(
colour=await ctx.embed_colour(),
title=_("Playing in {num}/{total} servers:").format(
num=server_num, total=total_num
num=humanize_number(server_num), total=humanize_number(total_num)
),
description=page,
)
em.set_footer(text="Page {}/{}".format(pages, (math.ceil(len(msg) / 1500))))
em.set_footer(
text="Page {}/{}".format(
humanize_number(pages), humanize_number((math.ceil(len(msg) / 1500)))
)
)
pages += 1
servers_embed.append(em)
@ -933,7 +939,9 @@ class Audio(commands.Cog):
embed = discord.Embed(
colour=await ctx.embed_colour(), description=(f"{header}\n{formatted_page}")
)
embed.set_footer(text=_("{num} preset(s)").format(num=len(list(eq_presets.keys()))))
embed.set_footer(
text=_("{num} preset(s)").format(num=humanize_number(len(list(eq_presets.keys()))))
)
page_list.append(embed)
if len(page_list) == 1:
return await ctx.send(embed=page_list[0])
@ -1869,6 +1877,7 @@ class Audio(commands.Cog):
song_info = "{} {}".format(i["name"], i["artists"][0]["name"])
else:
song_info = "{} {}".format(i["track"]["name"], i["track"]["artists"][0]["name"])
try:
track_url = await self._youtube_api_search(yt_key, song_info)
except (RuntimeError, aiohttp.client_exceptions.ServerDisconnectedError):
@ -1878,7 +1887,6 @@ class Audio(commands.Cog):
)
await playlist_msg.edit(embed=error_embed)
return None
pass
try:
yt_track = await player.get_tracks(track_url)
except (RuntimeError, aiohttp.client_exceptions.ServerDisconnectedError):
@ -3416,8 +3424,8 @@ class Audio(commands.Cog):
" Votes: {num_votes}/{num_members}"
" ({cur_percent}% out of {required_percent}% needed)"
).format(
num_votes=num_votes,
num_members=num_members,
num_votes=humanize_number(num_votes),
num_members=humanize_number(num_members),
cur_percent=vote,
required_percent=percent,
)
@ -3869,7 +3877,7 @@ class Audio(commands.Cog):
await self._embed_msg(
ctx,
_("Not enough {currency} ({required_credits} required).").format(
currency=credits_name, required_credits=jukebox_price
currency=credits_name, required_credits=humanize_number(jukebox_price)
),
)
return False

View File

@ -1,5 +1,5 @@
import discord
from redbot.core.utils.chat_formatting import box
from redbot.core.utils.chat_formatting import box, humanize_number
from redbot.core import checks, bank, commands
from redbot.core.i18n import Translator, cog_i18n
@ -88,7 +88,9 @@ class Bank(commands.Cog):
"Bank settings:\n\nBank name: {bank_name}\nCurrency: {currency_name}\n"
"Default balance: {default_balance}"
).format(
bank_name=bank_name, currency_name=currency_name, default_balance=default_balance
bank_name=bank_name,
currency_name=currency_name,
default_balance=humanize_number(default_balance),
)
await ctx.send(box(settings))

View File

@ -8,6 +8,7 @@ import discord
from redbot.core import checks, commands
from redbot.core.bot import Red
from redbot.core.i18n import Translator, cog_i18n
from redbot.core.utils.chat_formatting import humanize_number
from redbot.core.utils.mod import slow_deletion, mass_purge
from redbot.core.utils.predicates import MessagePredicate
from .converters import RawMessageIds
@ -39,7 +40,9 @@ class Cleanup(commands.Cog):
return True
prompt = await ctx.send(
_("Are you sure you want to delete {number} messages? (y/n)").format(number=number)
_("Are you sure you want to delete {number} messages? (y/n)").format(
number=humanize_number(number)
)
)
response = await ctx.bot.wait_for("message", check=MessagePredicate.same_context(ctx))
@ -152,7 +155,11 @@ class Cleanup(commands.Cog):
to_delete.append(ctx.message)
reason = "{}({}) deleted {} messages containing '{}' in channel {}.".format(
author.name, author.id, len(to_delete), text, channel.id
author.name,
author.id,
humanize_number(len(to_delete), override_locale="en_us"),
text,
channel.id,
)
log.info(reason)
@ -208,7 +215,14 @@ class Cleanup(commands.Cog):
reason = (
"{}({}) deleted {} messages "
" made by {}({}) in channel {}."
"".format(author.name, author.id, len(to_delete), member or "???", _id, channel.name)
"".format(
author.name,
author.id,
humanize_number(len(to_delete), override_locale="en_US"),
member or "???",
_id,
channel.name,
)
)
log.info(reason)
@ -240,7 +254,10 @@ class Cleanup(commands.Cog):
)
reason = "{}({}) deleted {} messages in channel {}.".format(
author.name, author.id, len(to_delete), channel.name
author.name,
author.id,
humanize_number(len(to_delete), override_locale="en_US"),
channel.name,
)
log.info(reason)
@ -277,7 +294,10 @@ class Cleanup(commands.Cog):
to_delete.append(ctx.message)
reason = "{}({}) deleted {} messages in channel {}.".format(
author.name, author.id, len(to_delete), channel.name
author.name,
author.id,
humanize_number(len(to_delete), override_locale="en_US"),
channel.name,
)
log.info(reason)
@ -319,7 +339,10 @@ class Cleanup(commands.Cog):
)
to_delete.append(ctx.message)
reason = "{}({}) deleted {} messages in channel {}.".format(
author.name, author.id, len(to_delete), channel.name
author.name,
author.id,
humanize_number(len(to_delete), override_locale="en_US"),
channel.name,
)
log.info(reason)
@ -420,7 +443,12 @@ class Cleanup(commands.Cog):
reason = (
"{}({}) deleted {} "
" command messages in channel {}."
"".format(author.name, author.id, len(to_delete), channel.name)
"".format(
author.name,
author.id,
humanize_number(len(to_delete), override_locale="en_US"),
channel.name,
)
)
log.info(reason)
@ -500,7 +528,12 @@ class Cleanup(commands.Cog):
reason = (
"{}({}) deleted {} messages "
"sent by the bot in {}."
"".format(author.name, author.id, len(to_delete), channel_name)
"".format(
author.name,
author.id,
humanize_number(len(to_delete), override_locale="en_US"),
channel_name,
)
)
log.info(reason)

View File

@ -10,7 +10,7 @@ import discord
from redbot.cogs.bank import check_global_setting_guildowner, check_global_setting_admin
from redbot.core import Config, bank, commands, errors
from redbot.core.i18n import Translator, cog_i18n
from redbot.core.utils.chat_formatting import box
from redbot.core.utils.chat_formatting import box, humanize_number
from redbot.core.utils.menus import menu, DEFAULT_CONTROLS
from redbot.core.bot import Red
@ -162,7 +162,7 @@ class Economy(commands.Cog):
await ctx.send(
_("{user}'s balance is {num} {currency}").format(
user=user.display_name, num=bal, currency=currency
user=user.display_name, num=humanize_number(bal), currency=currency
)
)
@ -179,7 +179,10 @@ class Economy(commands.Cog):
await ctx.send(
_("{user} transferred {num} {currency} to {other_user}").format(
user=from_.display_name, num=amount, currency=currency, other_user=to.display_name
user=from_.display_name,
num=humanize_number(amount),
currency=currency,
other_user=to.display_name,
)
)
@ -203,7 +206,7 @@ class Economy(commands.Cog):
await bank.deposit_credits(to, creds.sum)
msg = _("{author} added {num} {currency} to {user}'s account.").format(
author=author.display_name,
num=creds.sum,
num=humanize_number(creds.sum),
currency=currency,
user=to.display_name,
)
@ -211,7 +214,7 @@ class Economy(commands.Cog):
await bank.withdraw_credits(to, creds.sum)
msg = _("{author} removed {num} {currency} from {user}'s account.").format(
author=author.display_name,
num=creds.sum,
num=humanize_number(creds.sum),
currency=currency,
user=to.display_name,
)
@ -219,7 +222,7 @@ class Economy(commands.Cog):
await bank.set_balance(to, creds.sum)
msg = _("{author} set {user}'s account balance to {num} {currency}.").format(
author=author.display_name,
num=creds.sum,
num=humanize_number(creds.sum),
currency=currency,
user=to.display_name,
)
@ -271,7 +274,9 @@ class Economy(commands.Cog):
"You've reached the maximum amount of {currency}!"
"Please spend some more \N{GRIMACING FACE}\n\n"
"You currently have {new_balance} {currency}."
).format(currency=credits_name, new_balance=exc.max_balance)
).format(
currency=credits_name, new_balance=humanize_number(exc.max_balance)
)
)
return
next_payday = cur_time + await self.config.PAYDAY_TIME()
@ -287,9 +292,9 @@ class Economy(commands.Cog):
).format(
author=author,
currency=credits_name,
amount=await self.config.PAYDAY_CREDITS(),
new_balance=await bank.get_balance(author),
pos=pos,
amount=humanize_number(await self.config.PAYDAY_CREDITS()),
new_balance=humanize_number(await bank.get_balance(author)),
pos=humanize_number(pos) if pos else pos,
)
)
@ -319,7 +324,9 @@ class Economy(commands.Cog):
"You've reached the maximum amount of {currency}! "
"Please spend some more \N{GRIMACING FACE}\n\n"
"You currently have {new_balance} {currency}."
).format(currency=credits_name, new_balance=exc.max_balance)
).format(
currency=credits_name, new_balance=humanize_number(exc.max_balance)
)
)
return
next_payday = cur_time + await self.config.guild(guild).PAYDAY_TIME()
@ -334,9 +341,9 @@ class Economy(commands.Cog):
).format(
author=author,
currency=credits_name,
amount=credit_amount,
new_balance=await bank.get_balance(author),
pos=pos,
amount=humanize_number(credit_amount),
new_balance=humanize_number(await bank.get_balance(author)),
pos=humanize_number(pos) if pos else pos,
)
)
else:
@ -364,7 +371,7 @@ class Economy(commands.Cog):
else:
bank_sorted = await bank.get_leaderboard(positions=top, guild=guild)
try:
bal_len = len(str(bank_sorted[0][1]["balance"]))
bal_len = len(humanize_number(bank_sorted[0][1]["balance"]))
# first user is the largest we'll see
except IndexError:
return await ctx.send(_("There are no accounts in the bank."))
@ -387,14 +394,17 @@ class Economy(commands.Cog):
if await ctx.bot.is_owner(ctx.author):
user_id = f"({str(acc[0])})"
name = f"{acc[1]['name']} {user_id}"
balance = acc[1]["balance"]
balance = humanize_number(acc[1]["balance"])
if acc[0] != author.id:
temp_msg += f"{f'{pos}.': <{pound_len+2}} {balance: <{bal_len + 5}} {name}\n"
temp_msg += (
f"{f'{humanize_number(pos)}.': <{pound_len+2}} "
f"{balance: <{bal_len + 5}} {name}\n"
)
else:
temp_msg += (
f"{f'{pos}.': <{pound_len+2}} "
f"{f'{humanize_number(pos)}.': <{pound_len+2}} "
f"{balance: <{bal_len + 5}} "
f"<<{author.display_name}>>\n"
)
@ -503,8 +513,8 @@ class Economy(commands.Cog):
"Please spend some more \N{GRIMACING FACE}\n{old_balance} -> {new_balance}!"
).format(
currency=await bank.get_currency_name(getattr(channel, "guild", None)),
old_balance=then,
new_balance=exc.max_balance,
old_balance=humanize_number(then),
new_balance=humanize_number(exc.max_balance),
)
)
return
@ -523,10 +533,10 @@ class Economy(commands.Cog):
slot=slot,
author=author,
phrase=phrase,
bid=bid,
old_balance=then,
new_balance=now,
pay=pay,
bid=humanize_number(bid),
old_balance=humanize_number(then),
new_balance=humanize_number(now),
pay=humanize_number(pay),
)
)
@ -552,12 +562,12 @@ class Economy(commands.Cog):
"Payday cooldown: {payday_time}\n"
"Amount given at account registration: {register_amount}"
).format(
slot_min=await conf.SLOT_MIN(),
slot_max=await conf.SLOT_MAX(),
slot_time=await conf.SLOT_TIME(),
payday_time=await conf.PAYDAY_TIME(),
payday_amount=await conf.PAYDAY_CREDITS(),
register_amount=await bank.get_default_balance(guild),
slot_min=humanize_number(await conf.SLOT_MIN()),
slot_max=humanize_number(await conf.SLOT_MAX()),
slot_time=humanize_number(await conf.SLOT_TIME()),
payday_time=humanize_number(await conf.PAYDAY_TIME()),
payday_amount=humanize_number(await conf.PAYDAY_CREDITS()),
register_amount=humanize_number(await bank.get_default_balance(guild)),
)
)
)
@ -575,7 +585,9 @@ class Economy(commands.Cog):
await self.config.guild(guild).SLOT_MIN.set(bid)
credits_name = await bank.get_currency_name(guild)
await ctx.send(
_("Minimum bid is now {bid} {currency}.").format(bid=bid, currency=credits_name)
_("Minimum bid is now {bid} {currency}.").format(
bid=humanize_number(bid), currency=credits_name
)
)
@economyset.command()
@ -594,7 +606,9 @@ class Economy(commands.Cog):
else:
await self.config.guild(guild).SLOT_MAX.set(bid)
await ctx.send(
_("Maximum bid is now {bid} {currency}.").format(bid=bid, currency=credits_name)
_("Maximum bid is now {bid} {currency}.").format(
bid=humanize_number(bid), currency=credits_name
)
)
@economyset.command()
@ -635,7 +649,7 @@ class Economy(commands.Cog):
await self.config.guild(guild).PAYDAY_CREDITS.set(creds)
await ctx.send(
_("Every payday will now give {num} {currency}.").format(
num=creds, currency=credits_name
num=humanize_number(creds), currency=credits_name
)
)
@ -655,7 +669,7 @@ class Economy(commands.Cog):
_(
"Every payday will now give {num} {currency} "
"to people with the role {role_name}."
).format(num=creds, currency=credits_name, role_name=role.name)
).format(num=humanize_number(creds), currency=credits_name, role_name=role.name)
)
@economyset.command()
@ -668,7 +682,7 @@ class Economy(commands.Cog):
await bank.set_default_balance(creds, guild)
await ctx.send(
_("Registering an account will now give {num} {currency}.").format(
num=creds, currency=credits_name
num=humanize_number(creds), currency=credits_name
)
)

View File

@ -7,7 +7,7 @@ import discord
from redbot.core import commands
from redbot.core.i18n import Translator, cog_i18n
from redbot.core.utils.menus import menu, DEFAULT_CONTROLS
from redbot.core.utils.chat_formatting import escape, italics
from redbot.core.utils.chat_formatting import escape, italics, humanize_number
_ = T_ = Translator("General", __file__)
@ -89,7 +89,11 @@ class General(commands.Cog):
author = ctx.author
if number > 1:
n = randint(1, number)
await ctx.send("{author.mention} :game_die: {n} :game_die:".format(author=author, n=n))
await ctx.send(
"{author.mention} :game_die: {n} :game_die:".format(
author=author, n=humanize_number(n)
)
)
else:
await ctx.send(_("{author.mention} Maybe higher than 1? ;P").format(author=author))
@ -223,10 +227,12 @@ class General(commands.Cog):
async def serverinfo(self, ctx):
"""Show server information."""
guild = ctx.guild
online = len([m.status for m in guild.members if m.status != discord.Status.offline])
total_users = len(guild.members)
text_channels = len(guild.text_channels)
voice_channels = len(guild.voice_channels)
online = humanize_number(
len([m.status for m in guild.members if m.status != discord.Status.offline])
)
total_users = humanize_number(len(guild.members))
text_channels = humanize_number(len(guild.text_channels))
voice_channels = humanize_number(len(guild.voice_channels))
passed = (ctx.message.created_at - guild.created_at).days
created_at = _("Since {date}. That's over {num} days ago!").format(
date=guild.created_at.strftime("%d %b %Y %H:%M"), num=passed
@ -234,9 +240,9 @@ class General(commands.Cog):
data = discord.Embed(description=created_at, colour=(await ctx.embed_colour()))
data.add_field(name=_("Region"), value=str(guild.region))
data.add_field(name=_("Users"), value=f"{online}/{total_users}")
data.add_field(name=_("Text Channels"), value=str(text_channels))
data.add_field(name=_("Voice Channels"), value=str(voice_channels))
data.add_field(name=_("Roles"), value=str(len(guild.roles)))
data.add_field(name=_("Text Channels"), value=text_channels)
data.add_field(name=_("Voice Channels"), value=voice_channels)
data.add_field(name=_("Roles"), value=humanize_number(len(guild.roles)))
data.add_field(name=_("Owner"), value=str(guild.owner))
data.set_footer(text=_("Server ID: ") + str(guild.id))

View File

@ -7,7 +7,7 @@ from typing import cast, Optional, Union
import discord
from redbot.core import commands, i18n, checks, modlog
from redbot.core.utils.chat_formatting import pagify
from redbot.core.utils.chat_formatting import pagify, humanize_number
from redbot.core.utils.mod import is_allowed_by_hierarchy, get_audit_reason
from .abc import MixinMeta
from .converters import RawUserIds
@ -244,7 +244,9 @@ class KickBanMixin(MixinMeta):
errors = {}
async def show_results():
text = _("Banned {num} users from the server.").format(num=len(banned))
text = _("Banned {num} users from the server.").format(
num=humanize_number(len(banned))
)
if errors:
text += _("\nErrors:\n")
text += "\n".join(errors.values())

View File

@ -6,7 +6,7 @@ from collections import Counter
import discord
from redbot.core import bank
from redbot.core.i18n import Translator
from redbot.core.utils.chat_formatting import box, bold, humanize_list
from redbot.core.utils.chat_formatting import box, bold, humanize_list, humanize_number
from redbot.core.utils.common_filters import normalize_smartquotes
from .log import LOG
@ -292,7 +292,7 @@ class TriviaSession:
" for coming first."
).format(
user=winner.display_name,
num=amount,
num=humanize_number(amount),
currency=await bank.get_currency_name(self.ctx.guild),
)
)

View File

@ -5,6 +5,7 @@ from functools import wraps
import discord
from redbot.core.utils.chat_formatting import humanize_number
from . import Config, errors, commands
from .i18n import Translator
@ -237,11 +238,20 @@ async def withdraw_credits(member: discord.Member, amount: int) -> int:
if not isinstance(amount, int):
raise TypeError("Withdrawal amount must be of type int, not {}.".format(type(amount)))
if _invalid_amount(amount):
raise ValueError("Invalid withdrawal amount {} < 0".format(amount))
raise ValueError(
"Invalid withdrawal amount {} < 0".format(
humanize_number(amount, override_locale="en_US")
)
)
bal = await get_balance(member)
if amount > bal:
raise ValueError("Insufficient funds {} > {}".format(amount, bal))
raise ValueError(
"Insufficient funds {} > {}".format(
humanize_number(amount, override_locale="en_US"),
humanize_number(bal, override_locale="en_US"),
)
)
return await set_balance(member, bal - amount)
@ -272,7 +282,11 @@ async def deposit_credits(member: discord.Member, amount: int) -> int:
if not isinstance(amount, int):
raise TypeError("Deposit amount must be of type int, not {}.".format(type(amount)))
if _invalid_amount(amount):
raise ValueError("Invalid deposit amount {} <= 0".format(amount))
raise ValueError(
"Invalid deposit amount {} <= 0".format(
humanize_number(amount, override_locale="en_US")
)
)
bal = await get_balance(member)
return await set_balance(member, amount + bal)
@ -309,7 +323,11 @@ async def transfer_credits(from_: discord.Member, to: discord.Member, amount: in
if not isinstance(amount, int):
raise TypeError("Transfer amount must be of type int, not {}.".format(type(amount)))
if _invalid_amount(amount):
raise ValueError("Invalid transfer amount {} <= 0".format(amount))
raise ValueError(
"Invalid transfer amount {} <= 0".format(
humanize_number(amount, override_locale="en_US")
)
)
if await get_balance(to) + amount > MAX_BALANCE:
currency = await get_currency_name(to.guild)
@ -727,7 +745,7 @@ def cost(amount: int):
credits_name = await get_currency_name(context.guild)
raise commands.UserFeedbackCheckFailure(
_("You need at least {cost} {currency} to use this command.").format(
cost=amount, currency=credits_name
cost=humanize_number(amount), currency=credits_name
)
)
else:

View File

@ -2,6 +2,7 @@ import importlib.machinery
import discord
from redbot.core.utils.chat_formatting import humanize_number
from .i18n import Translator
_ = Translator(__name__, __file__)
@ -45,8 +46,8 @@ class BalanceTooHigh(BankError, OverflowError):
self.currency_name = currency_name
def __str__(self) -> str:
return _("{user}'s balance cannot rise above {max:,} {currency}.").format(
user=self.user, max=self.max_balance, currency=self.currency_name
return _("{user}'s balance cannot rise above max {currency}.").format(
user=self.user, max=humanize_number(self.max_balance), currency=self.currency_name
)

View File

@ -1,10 +1,21 @@
import contextlib
import functools
import io
import os
from pathlib import Path
from typing import Callable, Union, Dict
from typing import Callable, Union, Dict, Optional
__all__ = ["get_locale", "set_locale", "reload_locales", "cog_i18n", "Translator"]
import babel.localedata
from babel.core import Locale
__all__ = [
"get_locale",
"set_locale",
"reload_locales",
"cog_i18n",
"Translator",
"get_babel_locale",
]
_current_locale = "en-US"
@ -160,6 +171,44 @@ class Translator(Callable[[str], str]):
self.translations[untranslated] = translated
@functools.lru_cache()
def _get_babel_locale(red_locale: str) -> babel.core.Locale:
supported_locales = babel.localedata.locale_identifiers()
try: # Handles cases where red_locale is already Babel supported
babel_locale = Locale(*babel.parse_locale(red_locale))
except (ValueError, babel.core.UnknownLocaleError):
try:
babel_locale = Locale(*babel.parse_locale(red_locale, sep="-"))
except (ValueError, babel.core.UnknownLocaleError):
# ValueError is Raised by `parse_locale` when an invalid Locale is given to it
# Lets handle it silently and default to "en_US"
try:
# Try to find a babel locale that's close to the one used by red
babel_locale = Locale(Locale.negotiate([red_locale], supported_locales, sep="-"))
except (ValueError, TypeError, babel.core.UnknownLocaleError):
# If we fail to get a close match we will then default to "en_US"
babel_locale = Locale("en", "US")
return babel_locale
def get_babel_locale(locale: Optional[str] = None) -> babel.core.Locale:
"""Function to convert a locale to a ``babel.core.Locale``.
Parameters
----------
locale : Optional[str]
The locale to convert, if not specified it defaults to the bot's locale.
Returns
-------
babel.core.Locale
The babel locale object.
"""
if locale is None:
locale = get_locale()
return _get_babel_locale(locale)
# This import to be down here to avoid circular import issues.
# This will be cleaned up at a later date
# noinspection PyPep8

View File

@ -1,11 +1,13 @@
import itertools
import datetime
from typing import Sequence, Iterator, List, Optional
from typing import Sequence, Iterator, List, Optional, Union
from io import BytesIO
import discord
from redbot.core.i18n import Translator
import discord
from babel.numbers import format_decimal
from redbot.core.i18n import Translator, get_babel_locale
_ = Translator("UtilsChatFormatting", __file__)
@ -432,6 +434,25 @@ def humanize_timedelta(
return ", ".join(strings)
def humanize_number(val: Union[int, float], override_locale=None) -> str:
"""
Convert an int or float to a str with digit separators based on bot locale
Parameters
----------
val : Union[int, float]
The int/float to be formatted.
override_locale: Optional[str]
A value to override the bots locale.
Returns
-------
str
locale aware formatted number.
"""
return format_decimal(val, locale=get_babel_locale(override_locale))
def text_to_file(
text: str, filename: str = "file.txt", *, spoiler: bool = False, encoding: str = "utf-8"
):

View File

@ -31,6 +31,7 @@ install_requires =
appdirs==1.4.3
async-timeout==3.0.1
attrs==19.1.0
babel==2.7.0
chardet==3.0.4
Click==7.0
colorama==0.4.1

View File

@ -9,6 +9,7 @@ install_requires =
aiohttp
aiohttp-json-rpc
appdirs
babel
click
colorama
discord.py