mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-12-07 01:42:30 -05:00
[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:
@@ -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:
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
):
|
||||
|
||||
Reference in New Issue
Block a user