mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-05 18:58:53 -05:00
Split public and private i18n APIs (#6022)
This commit is contained in:
parent
8b1daf1ad0
commit
a0c1713e78
1
.github/labeler.yml
vendored
1
.github/labeler.yml
vendored
@ -216,6 +216,7 @@
|
||||
- redbot/core/commands/help.py
|
||||
"Category: Core - i18n":
|
||||
# Source
|
||||
- redbot/core/_i18n.py
|
||||
- redbot/core/i18n.py
|
||||
# Locale files
|
||||
- redbot/**/locales/*
|
||||
|
||||
79
redbot/core/_i18n.py
Normal file
79
redbot/core/_i18n.py
Normal file
@ -0,0 +1,79 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from contextvars import ContextVar
|
||||
from typing import TYPE_CHECKING, List, Optional
|
||||
|
||||
from babel.core import Locale, UnknownLocaleError
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from redbot.core.i18n import Translator
|
||||
|
||||
|
||||
__all__ = (
|
||||
"current_locale",
|
||||
"current_locale_default",
|
||||
"current_regional_format",
|
||||
"current_regional_format_default",
|
||||
"translators",
|
||||
"set_global_locale",
|
||||
"set_global_regional_format",
|
||||
"set_contextual_locale",
|
||||
"set_contextual_regional_format",
|
||||
)
|
||||
|
||||
|
||||
current_locale = ContextVar("current_locale")
|
||||
current_locale_default = "en-US"
|
||||
current_regional_format = ContextVar("current_regional_format")
|
||||
current_regional_format_default = None
|
||||
|
||||
translators: List[Translator] = []
|
||||
|
||||
|
||||
def _reload_locales() -> None:
|
||||
for translator in translators:
|
||||
translator.load_translations()
|
||||
|
||||
|
||||
def _get_standardized_locale_name(language_code: str) -> str:
|
||||
try:
|
||||
locale = Locale.parse(language_code, sep="-")
|
||||
except (ValueError, UnknownLocaleError):
|
||||
raise ValueError("Invalid language code. Use format: `en-US`")
|
||||
if locale.territory is None:
|
||||
raise ValueError(
|
||||
"Invalid format - language code has to include country code, e.g. `en-US`"
|
||||
)
|
||||
return f"{locale.language}-{locale.territory}"
|
||||
|
||||
|
||||
def set_global_locale(language_code: str, /) -> str:
|
||||
global current_locale_default
|
||||
current_locale_default = _get_standardized_locale_name(language_code)
|
||||
_reload_locales()
|
||||
return current_locale_default
|
||||
|
||||
|
||||
def set_global_regional_format(language_code: Optional[str], /) -> Optional[str]:
|
||||
global current_regional_format_default
|
||||
if language_code is not None:
|
||||
language_code = _get_standardized_locale_name(language_code)
|
||||
current_regional_format_default = language_code
|
||||
return language_code
|
||||
|
||||
|
||||
def set_contextual_locale(language_code: str, /, verify_language_code: bool = False) -> str:
|
||||
if verify_language_code:
|
||||
language_code = _get_standardized_locale_name(language_code)
|
||||
current_locale.set(language_code)
|
||||
_reload_locales()
|
||||
return language_code
|
||||
|
||||
|
||||
def set_contextual_regional_format(
|
||||
language_code: str, /, verify_language_code: bool = False
|
||||
) -> str:
|
||||
if verify_language_code and language_code is not None:
|
||||
language_code = _get_standardized_locale_name(language_code)
|
||||
current_regional_format.set(language_code)
|
||||
return language_code
|
||||
@ -37,7 +37,7 @@ import discord
|
||||
from discord.ext import commands as dpy_commands
|
||||
from discord.ext.commands import when_mentioned_or
|
||||
|
||||
from . import Config, i18n, app_commands, commands, errors, _drivers, modlog, bank
|
||||
from . import Config, _i18n, i18n, app_commands, commands, errors, _drivers, modlog, bank
|
||||
from ._cli import ExitCodes
|
||||
from ._cog_manager import CogManager, CogManagerUI
|
||||
from .core_commands import Core
|
||||
@ -1145,9 +1145,9 @@ class Red(
|
||||
self.owner_ids.add(self._owner_id_overwrite)
|
||||
|
||||
i18n_locale = await self._config.locale()
|
||||
i18n.set_locale(i18n_locale)
|
||||
_i18n.set_global_locale(i18n_locale)
|
||||
i18n_regional_format = await self._config.regional_format()
|
||||
i18n.set_regional_format(i18n_regional_format)
|
||||
_i18n.set_global_regional_format(i18n_regional_format)
|
||||
|
||||
async def _pre_connect(self) -> None:
|
||||
"""
|
||||
|
||||
@ -38,7 +38,6 @@ from typing import (
|
||||
|
||||
import aiohttp
|
||||
import discord
|
||||
from babel import Locale as BabelLocale, UnknownLocaleError
|
||||
from redbot.core.data_manager import storage_type
|
||||
|
||||
from . import (
|
||||
@ -46,6 +45,7 @@ from . import (
|
||||
version_info as red_version_info,
|
||||
commands,
|
||||
errors,
|
||||
_i18n,
|
||||
i18n,
|
||||
bank,
|
||||
modlog,
|
||||
@ -3522,17 +3522,10 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
|
||||
- `<language_code>` - The default locale to use for the bot. This can be any language code with country code included.
|
||||
"""
|
||||
try:
|
||||
locale = BabelLocale.parse(language_code, sep="-")
|
||||
except (ValueError, UnknownLocaleError):
|
||||
standardized_locale_name = _i18n.set_global_locale(language_code)
|
||||
except ValueError:
|
||||
await ctx.send(_("Invalid language code. Use format: `en-US`"))
|
||||
return
|
||||
if locale.territory is None:
|
||||
await ctx.send(
|
||||
_("Invalid format - language code has to include country code, e.g. `en-US`")
|
||||
)
|
||||
return
|
||||
standardized_locale_name = f"{locale.language}-{locale.territory}"
|
||||
i18n.set_locale(standardized_locale_name)
|
||||
await self.bot._i18n_cache.set_locale(None, standardized_locale_name)
|
||||
await i18n.set_contextual_locales_from_guild(self.bot, ctx.guild)
|
||||
await ctx.send(_("Global locale has been set."))
|
||||
@ -3565,17 +3558,10 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
|
||||
await ctx.send(_("Locale has been set to the default."))
|
||||
return
|
||||
try:
|
||||
locale = BabelLocale.parse(language_code, sep="-")
|
||||
except (ValueError, UnknownLocaleError):
|
||||
standardized_locale_name = i18n.set_contextual_locale(language_code)
|
||||
except ValueError:
|
||||
await ctx.send(_("Invalid language code. Use format: `en-US`"))
|
||||
return
|
||||
if locale.territory is None:
|
||||
await ctx.send(
|
||||
_("Invalid format - language code has to include country code, e.g. `en-US`")
|
||||
)
|
||||
return
|
||||
standardized_locale_name = f"{locale.language}-{locale.territory}"
|
||||
i18n.set_contextual_locale(standardized_locale_name)
|
||||
await self.bot._i18n_cache.set_locale(ctx.guild, standardized_locale_name)
|
||||
await ctx.send(_("Locale has been set."))
|
||||
|
||||
@ -3621,23 +3607,16 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
|
||||
- `[language_code]` - The default region format to use for the bot.
|
||||
"""
|
||||
if language_code.lower() == "reset":
|
||||
i18n.set_regional_format(None)
|
||||
_i18n.set_global_regional_format(None)
|
||||
await self.bot._i18n_cache.set_regional_format(None, None)
|
||||
await ctx.send(_("Global regional formatting will now be based on bot's locale."))
|
||||
return
|
||||
|
||||
try:
|
||||
locale = BabelLocale.parse(language_code, sep="-")
|
||||
except (ValueError, UnknownLocaleError):
|
||||
standardized_locale_name = _i18n.set_global_regional_format(language_code)
|
||||
except ValueError:
|
||||
await ctx.send(_("Invalid language code. Use format: `en-US`"))
|
||||
return
|
||||
if locale.territory is None:
|
||||
await ctx.send(
|
||||
_("Invalid format - language code has to include country code, e.g. `en-US`")
|
||||
)
|
||||
return
|
||||
standardized_locale_name = f"{locale.language}-{locale.territory}"
|
||||
i18n.set_regional_format(standardized_locale_name)
|
||||
await self.bot._i18n_cache.set_regional_format(None, standardized_locale_name)
|
||||
await ctx.send(
|
||||
_("Global regional formatting will now be based on `{language_code}` locale.").format(
|
||||
@ -3672,17 +3651,10 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
|
||||
return
|
||||
|
||||
try:
|
||||
locale = BabelLocale.parse(language_code, sep="-")
|
||||
except (ValueError, UnknownLocaleError):
|
||||
standardized_locale_name = i18n.set_contextual_regional_format(language_code)
|
||||
except ValueError:
|
||||
await ctx.send(_("Invalid language code. Use format: `en-US`"))
|
||||
return
|
||||
if locale.territory is None:
|
||||
await ctx.send(
|
||||
_("Invalid format - language code has to include country code, e.g. `en-US`")
|
||||
)
|
||||
return
|
||||
standardized_locale_name = f"{locale.language}-{locale.territory}"
|
||||
i18n.set_contextual_regional_format(standardized_locale_name)
|
||||
await self.bot._i18n_cache.set_regional_format(ctx.guild, standardized_locale_name)
|
||||
await ctx.send(
|
||||
_("Regional formatting will now be based on `{language_code}` locale.").format(
|
||||
|
||||
@ -9,18 +9,27 @@ import discord
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Callable, TYPE_CHECKING, Union, Dict, Optional, TypeVar
|
||||
from contextvars import ContextVar
|
||||
|
||||
import babel.localedata
|
||||
from babel.core import Locale
|
||||
|
||||
from redbot.core import _i18n
|
||||
from redbot.core._i18n import (
|
||||
current_locale as _current_locale,
|
||||
current_regional_format as _current_regional_format,
|
||||
set_contextual_locale as _set_contextual_locale,
|
||||
set_contextual_regional_format as _set_contextual_regional_format,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from redbot.core.bot import Red
|
||||
|
||||
|
||||
__all__ = [
|
||||
__all__ = (
|
||||
"get_locale",
|
||||
"get_regional_format",
|
||||
"set_contextual_locale",
|
||||
"set_contextual_regional_format",
|
||||
"get_locale_from_guild",
|
||||
"get_regional_format_from_guild",
|
||||
"set_contextual_locales_from_guild",
|
||||
@ -28,13 +37,10 @@ __all__ = [
|
||||
"get_babel_locale",
|
||||
"get_babel_regional_format",
|
||||
"cog_i18n",
|
||||
]
|
||||
)
|
||||
|
||||
log = logging.getLogger("red.i18n")
|
||||
|
||||
_current_locale = ContextVar("_current_locale", default="en-US")
|
||||
_current_regional_format = ContextVar("_current_regional_format", default=None)
|
||||
|
||||
WAITING_FOR_MSGID = 1
|
||||
IN_MSGID = 2
|
||||
WAITING_FOR_MSGSTR = 3
|
||||
@ -43,8 +49,6 @@ IN_MSGSTR = 4
|
||||
MSGID = 'msgid "'
|
||||
MSGSTR = 'msgstr "'
|
||||
|
||||
_translators = []
|
||||
|
||||
|
||||
def get_locale() -> str:
|
||||
"""
|
||||
@ -55,18 +59,7 @@ def get_locale() -> str:
|
||||
str
|
||||
Current locale's language code with country code included, e.g. "en-US".
|
||||
"""
|
||||
return str(_current_locale.get())
|
||||
|
||||
|
||||
def set_locale(locale: str) -> None:
|
||||
global _current_locale
|
||||
_current_locale = ContextVar("_current_locale", default=locale)
|
||||
reload_locales()
|
||||
|
||||
|
||||
def set_contextual_locale(locale: str) -> None:
|
||||
_current_locale.set(locale)
|
||||
reload_locales()
|
||||
return _current_locale.get(_i18n.current_locale_default)
|
||||
|
||||
|
||||
def get_regional_format() -> str:
|
||||
@ -78,23 +71,55 @@ def get_regional_format() -> str:
|
||||
str
|
||||
Current regional format's language code with country code included, e.g. "en-US".
|
||||
"""
|
||||
if _current_regional_format.get() is None:
|
||||
return str(_current_locale.get())
|
||||
return str(_current_regional_format.get())
|
||||
regional_format = _current_regional_format.get(_i18n.current_regional_format_default)
|
||||
if regional_format is None:
|
||||
return _current_locale.get(_i18n.current_locale_default)
|
||||
return regional_format
|
||||
|
||||
|
||||
def set_regional_format(regional_format: Optional[str]) -> None:
|
||||
global _current_regional_format
|
||||
_current_regional_format = ContextVar("_current_regional_format", default=regional_format)
|
||||
def set_contextual_locale(language_code: str, /) -> str:
|
||||
"""
|
||||
Set contextual locale (without regional format) to the given value.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
language_code: str
|
||||
Locale's language code with country code included, e.g. "en-US".
|
||||
|
||||
Returns
|
||||
-------
|
||||
str
|
||||
Standardized locale name.
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
Language code is invalid.
|
||||
"""
|
||||
return _set_contextual_locale(language_code, verify_language_code=True)
|
||||
|
||||
|
||||
def set_contextual_regional_format(regional_format: Optional[str]) -> None:
|
||||
_current_regional_format.set(regional_format)
|
||||
def set_contextual_regional_format(language_code: Optional[str], /) -> Optional[str]:
|
||||
"""
|
||||
Set contextual regional format to the given value.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
language_code: str, optional
|
||||
Contextual regional's language code with country code included, e.g. "en-US"
|
||||
or ``None`` if regional format should inherit the contextual locale's value.
|
||||
|
||||
def reload_locales() -> None:
|
||||
for translator in _translators:
|
||||
translator.load_translations()
|
||||
Returns
|
||||
-------
|
||||
str
|
||||
Standardized locale name or ``None`` if ``None`` was passed.
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
Language code is invalid.
|
||||
"""
|
||||
return _set_contextual_regional_format(language_code, verify_language_code=True)
|
||||
|
||||
|
||||
async def get_locale_from_guild(bot: Red, guild: Optional[discord.Guild]) -> str:
|
||||
@ -151,8 +176,8 @@ async def set_contextual_locales_from_guild(bot: Red, guild: Optional[discord.Gu
|
||||
"""
|
||||
locale = await get_locale_from_guild(bot, guild)
|
||||
regional_format = await get_regional_format_from_guild(bot, guild)
|
||||
set_contextual_locale(locale)
|
||||
set_contextual_regional_format(regional_format)
|
||||
_set_contextual_locale(locale)
|
||||
_set_contextual_regional_format(regional_format)
|
||||
|
||||
|
||||
def _parse(translation_file: io.TextIOWrapper) -> Dict[str, str]:
|
||||
@ -216,7 +241,7 @@ def _unescape(string):
|
||||
return string
|
||||
|
||||
|
||||
def get_locale_path(cog_folder: Path, extension: str) -> Path:
|
||||
def _get_locale_path(cog_folder: Path, extension: str) -> Path:
|
||||
"""
|
||||
Gets the folder path containing localization files.
|
||||
|
||||
@ -250,7 +275,7 @@ class Translator(Callable[[str], str]):
|
||||
self.cog_name = name
|
||||
self.translations = {}
|
||||
|
||||
_translators.append(self)
|
||||
_i18n.translators.append(self)
|
||||
|
||||
self.load_translations()
|
||||
|
||||
@ -280,7 +305,7 @@ class Translator(Callable[[str], str]):
|
||||
# self.translations
|
||||
return
|
||||
|
||||
locale_path = get_locale_path(self.cog_folder, "po")
|
||||
locale_path = _get_locale_path(self.cog_folder, "po")
|
||||
with contextlib.suppress(IOError, FileNotFoundError):
|
||||
with locale_path.open(encoding="utf-8") as file:
|
||||
self._parse(file)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user