Skip to content

Commit

Permalink
Add support for option localization
Browse files Browse the repository at this point in the history
  • Loading branch information
Dorukyum committed Sep 8, 2022
1 parent 5740974 commit 5e8759a
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 21 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

## Key Features
- Translated responses
- Command name & description localization
- Command name, description & option localization
- Based on user & server locale (no need for storage!)

## Installation
Expand Down
71 changes: 52 additions & 19 deletions pycord/i18n/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
from typing import Dict, Literal, Optional, TypedDict, TypeVar, Union
from typing import Dict, Literal, TypedDict, TypeVar, Union

from discord import ApplicationContext, Bot, ContextMenuCommand, SlashCommand, utils

__all__ = ("Locale", "I18n", "_")
__all__ = (
"Locale",
"OptionLocalization",
"CommandLocalization",
"Internationalization",
"I18n",
"_",
)

Localizable = Union[SlashCommand, ContextMenuCommand]
CommandT = TypeVar("CommandT", bound=Localizable)
Expand Down Expand Up @@ -40,9 +47,18 @@
]


class OptionLocalization(TypedDict, total=False):
name: str
description: str


class CommandLocalization(OptionLocalization, total=False):
options: Dict[str, OptionLocalization]


class Internationalization(TypedDict, total=False):
strings: Dict[str, str]
commands: Dict[str, Dict[Literal["name", "description"], str]]
commands: Dict[str, CommandLocalization]


class I18n:
Expand All @@ -56,7 +72,7 @@ class I18n:
Whether to consider the user's locale when translating responses or not.
By default this is `False` and responses will be based on the server's locale.
**translations:
Key-value pairs of locales and translations based on the following format:
Key-value pairs of locales and translations based on the `Internationalization` typeddict.
.. code-block:: python
Expand All @@ -66,6 +82,12 @@ class I18n:
"help": {
"name": "hilfe",
"description": "...",
"options": {
"category": {
"name": "kategorie",
"description": "...",
}
}
}
}
}
Expand All @@ -86,7 +108,7 @@ def __init__(
for k, v in internalizations.items()
if (strings := v.get("strings"))
}
self.localizations: Dict[Locale, Dict[str, Dict[Literal["name", "description"], str]]] = { # type: ignore
self.localizations: Dict[Locale, Dict[str, CommandLocalization]] = { # type: ignore
k.replace("_", "-"): commands
for k, v in internalizations.items()
if (commands := v.get("commands"))
Expand All @@ -100,19 +122,32 @@ def _localize_command(
self,
command: Localizable,
locale: str,
name: Optional[str] = None,
description: Optional[str] = None,
localizations: CommandLocalization,
) -> None:
if name:
if command.name_localizations is not None:
command.name_localizations[locale] = name
else:
if name := localizations.get("name"):
if command.name_localizations is None:
command.name_localizations = {locale: name}
if isinstance(command, SlashCommand) and description:
if command.description_localizations is not None:
command.description_localizations[locale] = description
else:
command.description_localizations = {locale: description}
command.name_localizations[locale] = name
if isinstance(command, SlashCommand):
if description := localizations.get("description"):
if command.description_localizations is None:
command.description_localizations = {locale: description}
else:
command.description_localizations[locale] = description
if options := localizations.get("options"):
for option_name, localization in options.items():
if option := utils.get(command.options, name=option_name):
if op_name := localization.get("name"):
if option.name_localizations is None:
option.name_localizations = {locale: op_name}
else:
option.name_localizations[locale] = op_name
if op_description := localization.get("description"):
if option.description_localizations is None:
option.description_localizations = {locale: op_description}
else:
option.description_localizations[locale] = op_description

def localize(self, command: CommandT) -> CommandT:
"""A decorator to apply name and description localizations to a command."""
Expand All @@ -122,8 +157,7 @@ def localize(self, command: CommandT) -> CommandT:
self._localize_command(
command,
locale,
localizations.get("name"),
localizations.get("description"),
localizations,
)
return command

Expand All @@ -139,8 +173,7 @@ def localize_commands(self) -> None:
self._localize_command(
command,
locale,
localizations.get("name"),
localizations.get("description"),
localizations,
)

async def set_current_locale(self, ctx: ApplicationContext) -> None:
Expand Down
8 changes: 7 additions & 1 deletion sample-german.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@
"commands": {
"hello": {
"name": "hallo",
"description": "Sag hallo"
"description": "Sag hallo",
"options": {
"user": {
"name": "benutzer",
"description": "Ein Benutzer"
}
}
}
}
}

0 comments on commit 5e8759a

Please sign in to comment.