discord-localization 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- discord_localization-0.1.2/LICENSE +21 -0
- discord_localization-0.1.2/PKG-INFO +114 -0
- discord_localization-0.1.2/README.md +100 -0
- discord_localization-0.1.2/discord/ext/localization/__init__.py +25 -0
- discord_localization-0.1.2/discord/ext/localization/localization.py +137 -0
- discord_localization-0.1.2/discord_localization.egg-info/PKG-INFO +114 -0
- discord_localization-0.1.2/discord_localization.egg-info/SOURCES.txt +10 -0
- discord_localization-0.1.2/discord_localization.egg-info/dependency_links.txt +1 -0
- discord_localization-0.1.2/discord_localization.egg-info/top_level.txt +1 -0
- discord_localization-0.1.2/setup.cfg +4 -0
- discord_localization-0.1.2/setup.py +29 -0
- discord_localization-0.1.2/tests/test.py +5 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Pearoo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: discord-localization
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: A discord.py extension for command localization.
|
|
5
|
+
Author: Pearoo
|
|
6
|
+
Author-email: <contact@pearoo.dev>
|
|
7
|
+
Project-URL: Source, https://github.com/PearooXD/discord-localization
|
|
8
|
+
Keywords: discord.py,i18n,translation,localisation,localization
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
|
|
15
|
+
[Github](https://github.com/PearooXD/discord-localization) | [PyPI](https://pypi.org/project/discord-localization/)
|
|
16
|
+
|
|
17
|
+
--------
|
|
18
|
+
## Setting up
|
|
19
|
+
Start by downloading the package via pip.
|
|
20
|
+
```
|
|
21
|
+
python3 -m pip install discord-localization
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
After that, you can just import it into your file as a discord.py extension.
|
|
25
|
+
|
|
26
|
+
--------
|
|
27
|
+
## Examples
|
|
28
|
+
```py
|
|
29
|
+
from discord.ext import localization
|
|
30
|
+
import random
|
|
31
|
+
|
|
32
|
+
_ = localization.Localization("main.i18n.json", "en") # the first option is the relative path to the localization file. the second is the fallback/default language that is used if the actual language is not found.
|
|
33
|
+
dice = random.randint(1, 6)
|
|
34
|
+
language = input("What's your native language? / Was ist Deine Muttersprache? (en/de) >>> ")
|
|
35
|
+
|
|
36
|
+
print(_("dice_result", language, dice=dice))
|
|
37
|
+
```
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"en": {
|
|
41
|
+
"dice_result": "Your dice rolled {dice}!"
|
|
42
|
+
},
|
|
43
|
+
"de": {
|
|
44
|
+
"dice_result": "Dein Würfel hat eine {dice} gewürfelt!"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Integrating it into your Discord bot
|
|
50
|
+
```py
|
|
51
|
+
import discord
|
|
52
|
+
from discord.ext import commands, localization
|
|
53
|
+
|
|
54
|
+
bot = commands.Bot(command_prefix="!", intents=discord.Intents.all()) # hey guys, breaking the 4th wall here. please stop using Intents.default() just to set the message_content intent to True right after. Intents.all() does it for you.
|
|
55
|
+
_ = localization.Localization("main_localization.json", "en")
|
|
56
|
+
|
|
57
|
+
@bot.event
|
|
58
|
+
async def on_ready():
|
|
59
|
+
print("Bot is ready!")
|
|
60
|
+
|
|
61
|
+
@bot.command()
|
|
62
|
+
async def ping(ctx):
|
|
63
|
+
await ctx.reply(_("ping", ctx, latency=bot.latency * 1000))
|
|
64
|
+
```
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"en": {
|
|
68
|
+
"ping": "Pong! {latency}ms"
|
|
69
|
+
},
|
|
70
|
+
"en-US": {
|
|
71
|
+
"ping": "Pong! {latency}ms, but American! (because Discord makes a difference between en-US and en-UK locales - you can circumvent this by setting the default_locale)"
|
|
72
|
+
},
|
|
73
|
+
"en-UK": {
|
|
74
|
+
"ping": "Ping is {latency}ms, innit, bruv? (i'm sorry)"
|
|
75
|
+
},
|
|
76
|
+
"fr": {
|
|
77
|
+
"ping": "Bonjour! Ping is {latency}ms!"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
Explanation:
|
|
82
|
+
- By setting the default locale to "en", we won't have any issues with unfinished localizations. This way, we are also not required to define localizations for both "en-US" and "en-UK" (since Discord's API differentiates them, however, usually, bots don't.)
|
|
83
|
+
- We are passing a `latency` argument to the localization function (which is also available with `Localization._`! But calling the object is fine too.), which will be later used whenever we use `{latency}` in a localization.
|
|
84
|
+
- We are defining an `en` locale in the JSON file, even though both Discord's API and discord.py doesn't have such a locale. This is to make it more obvious that everything in that locale should be in English - you could also name it `default_locale`, you just have to make sure to edit it in the `Localization` object.
|
|
85
|
+
- We pass `ctx` to the localization function, which will automatically try getting `ctx.guild.preferred_locale`. This works with `Interaction` (by using `Interaction.guild.preferred_locale` internally), `Guild` (by using `guild.preferred_locale` internally), or just passing the `Locale` itself, for example, `discord.Locale.american_english` (which will convert to the `en-US` locale).
|
|
86
|
+
|
|
87
|
+
-------
|
|
88
|
+
## Working with plurals
|
|
89
|
+
```py
|
|
90
|
+
from discord.ext.localization import Localization
|
|
91
|
+
import locale
|
|
92
|
+
|
|
93
|
+
_ = Localization("main_plurals.json") # this will look for main_plurals.json as the language JSON file
|
|
94
|
+
apples = int(input("How many apples do you have? >>> "))
|
|
95
|
+
language = locale.getlocale()[0][:2] # this will return the default OS language, such as en, hu, de, fr, es etc.
|
|
96
|
+
|
|
97
|
+
print(_.one("apples", apples, language, apples=apples))
|
|
98
|
+
```
|
|
99
|
+
```json
|
|
100
|
+
{
|
|
101
|
+
"en": {
|
|
102
|
+
"apples": ["You only have one apple! What a loser...", "You have {apples} apples!"]
|
|
103
|
+
},
|
|
104
|
+
"hu": {
|
|
105
|
+
"apples": ["{apples} almád van!"]
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
This example does a great job at presenting how the `.one()` function works. Here's the explanation for the code:
|
|
111
|
+
|
|
112
|
+
The code will look for the default OS language, and will tell you how many apples you have with plurals in mind. I always get angry when I see "you have 1 apples"-ish text on a website, and it's an easy fix, but for some reason, many developers don't pay attention to it.
|
|
113
|
+
|
|
114
|
+
Hungarian doesn't make a difference between plurals, so the list for "hu"/"apples" only has 1 item. This won't be a problem though - the library isn't hardcoded to only look for the first and second elements in the returned list, it will look for the **first** and the **last**. Which means that if a list only has 1 item, it will only return that item.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
[Github](https://github.com/PearooXD/discord-localization) | [PyPI](https://pypi.org/project/discord-localization/)
|
|
2
|
+
|
|
3
|
+
--------
|
|
4
|
+
## Setting up
|
|
5
|
+
Start by downloading the package via pip.
|
|
6
|
+
```
|
|
7
|
+
python3 -m pip install discord-localization
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
After that, you can just import it into your file as a discord.py extension.
|
|
11
|
+
|
|
12
|
+
--------
|
|
13
|
+
## Examples
|
|
14
|
+
```py
|
|
15
|
+
from discord.ext import localization
|
|
16
|
+
import random
|
|
17
|
+
|
|
18
|
+
_ = localization.Localization("main.i18n.json", "en") # the first option is the relative path to the localization file. the second is the fallback/default language that is used if the actual language is not found.
|
|
19
|
+
dice = random.randint(1, 6)
|
|
20
|
+
language = input("What's your native language? / Was ist Deine Muttersprache? (en/de) >>> ")
|
|
21
|
+
|
|
22
|
+
print(_("dice_result", language, dice=dice))
|
|
23
|
+
```
|
|
24
|
+
```json
|
|
25
|
+
{
|
|
26
|
+
"en": {
|
|
27
|
+
"dice_result": "Your dice rolled {dice}!"
|
|
28
|
+
},
|
|
29
|
+
"de": {
|
|
30
|
+
"dice_result": "Dein Würfel hat eine {dice} gewürfelt!"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Integrating it into your Discord bot
|
|
36
|
+
```py
|
|
37
|
+
import discord
|
|
38
|
+
from discord.ext import commands, localization
|
|
39
|
+
|
|
40
|
+
bot = commands.Bot(command_prefix="!", intents=discord.Intents.all()) # hey guys, breaking the 4th wall here. please stop using Intents.default() just to set the message_content intent to True right after. Intents.all() does it for you.
|
|
41
|
+
_ = localization.Localization("main_localization.json", "en")
|
|
42
|
+
|
|
43
|
+
@bot.event
|
|
44
|
+
async def on_ready():
|
|
45
|
+
print("Bot is ready!")
|
|
46
|
+
|
|
47
|
+
@bot.command()
|
|
48
|
+
async def ping(ctx):
|
|
49
|
+
await ctx.reply(_("ping", ctx, latency=bot.latency * 1000))
|
|
50
|
+
```
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"en": {
|
|
54
|
+
"ping": "Pong! {latency}ms"
|
|
55
|
+
},
|
|
56
|
+
"en-US": {
|
|
57
|
+
"ping": "Pong! {latency}ms, but American! (because Discord makes a difference between en-US and en-UK locales - you can circumvent this by setting the default_locale)"
|
|
58
|
+
},
|
|
59
|
+
"en-UK": {
|
|
60
|
+
"ping": "Ping is {latency}ms, innit, bruv? (i'm sorry)"
|
|
61
|
+
},
|
|
62
|
+
"fr": {
|
|
63
|
+
"ping": "Bonjour! Ping is {latency}ms!"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
Explanation:
|
|
68
|
+
- By setting the default locale to "en", we won't have any issues with unfinished localizations. This way, we are also not required to define localizations for both "en-US" and "en-UK" (since Discord's API differentiates them, however, usually, bots don't.)
|
|
69
|
+
- We are passing a `latency` argument to the localization function (which is also available with `Localization._`! But calling the object is fine too.), which will be later used whenever we use `{latency}` in a localization.
|
|
70
|
+
- We are defining an `en` locale in the JSON file, even though both Discord's API and discord.py doesn't have such a locale. This is to make it more obvious that everything in that locale should be in English - you could also name it `default_locale`, you just have to make sure to edit it in the `Localization` object.
|
|
71
|
+
- We pass `ctx` to the localization function, which will automatically try getting `ctx.guild.preferred_locale`. This works with `Interaction` (by using `Interaction.guild.preferred_locale` internally), `Guild` (by using `guild.preferred_locale` internally), or just passing the `Locale` itself, for example, `discord.Locale.american_english` (which will convert to the `en-US` locale).
|
|
72
|
+
|
|
73
|
+
-------
|
|
74
|
+
## Working with plurals
|
|
75
|
+
```py
|
|
76
|
+
from discord.ext.localization import Localization
|
|
77
|
+
import locale
|
|
78
|
+
|
|
79
|
+
_ = Localization("main_plurals.json") # this will look for main_plurals.json as the language JSON file
|
|
80
|
+
apples = int(input("How many apples do you have? >>> "))
|
|
81
|
+
language = locale.getlocale()[0][:2] # this will return the default OS language, such as en, hu, de, fr, es etc.
|
|
82
|
+
|
|
83
|
+
print(_.one("apples", apples, language, apples=apples))
|
|
84
|
+
```
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"en": {
|
|
88
|
+
"apples": ["You only have one apple! What a loser...", "You have {apples} apples!"]
|
|
89
|
+
},
|
|
90
|
+
"hu": {
|
|
91
|
+
"apples": ["{apples} almád van!"]
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
This example does a great job at presenting how the `.one()` function works. Here's the explanation for the code:
|
|
97
|
+
|
|
98
|
+
The code will look for the default OS language, and will tell you how many apples you have with plurals in mind. I always get angry when I see "you have 1 apples"-ish text on a website, and it's an easy fix, but for some reason, many developers don't pay attention to it.
|
|
99
|
+
|
|
100
|
+
Hungarian doesn't make a difference between plurals, so the list for "hu"/"apples" only has 1 item. This won't be a problem though - the library isn't hardcoded to only look for the first and second elements in the returned list, it will look for the **first** and the **last**. Which means that if a list only has 1 item, it will only return that item.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MIT License
|
|
3
|
+
|
|
4
|
+
Copyright (c) 2024 Pearoo
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from .localization import Localization as Localization
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
import os
|
|
4
|
+
from typing import Any, List, Optional, Union
|
|
5
|
+
from sys import argv
|
|
6
|
+
from discord import Guild, Locale, Interaction
|
|
7
|
+
from discord.ext.commands import Context
|
|
8
|
+
|
|
9
|
+
class Localization:
|
|
10
|
+
"""discord.py extension for command localization."""
|
|
11
|
+
|
|
12
|
+
def __init__(self, relative_path: str, default_locale: Optional[str] = None, error: Optional[bool] = False) -> None:
|
|
13
|
+
"""
|
|
14
|
+
Represents a localization object.
|
|
15
|
+
|
|
16
|
+
-------
|
|
17
|
+
### Parameters:
|
|
18
|
+
relative_path: :class:`str`
|
|
19
|
+
The name of the file to load the localizations from. This must be a JSON file.
|
|
20
|
+
default_locale: Optional[:class:`str`]
|
|
21
|
+
The default locale to use if the locale is not found in the localization.
|
|
22
|
+
error: Optional[:class:`bool`]
|
|
23
|
+
Whether to raise an error if the localization is not found. If set to `False`, it will return the key itself. Defaults to `False`.
|
|
24
|
+
"""
|
|
25
|
+
script_path = os.path.abspath(argv[0])
|
|
26
|
+
script_dir = os.path.dirname(script_path)
|
|
27
|
+
|
|
28
|
+
self.relative_path: str = os.path.join(script_dir, relative_path)
|
|
29
|
+
self.default_locale: str = default_locale
|
|
30
|
+
self.error: bool = error
|
|
31
|
+
|
|
32
|
+
try:
|
|
33
|
+
with open(self.relative_path, "r", encoding="utf-8") as f:
|
|
34
|
+
self.file: dict = json.load(f)
|
|
35
|
+
except json.JSONDecodeError:
|
|
36
|
+
raise ValueError("invalid JSON format in translation file: {}".format(self.relative_path))
|
|
37
|
+
|
|
38
|
+
def localize(self, text: str, locale: Union[str, Locale, Guild, Interaction, Context], **kwargs: Any) -> Union[str, List[str]]:
|
|
39
|
+
"""
|
|
40
|
+
Gets the localization of a string like it's done in i18n.
|
|
41
|
+
|
|
42
|
+
-------
|
|
43
|
+
### Parameters:
|
|
44
|
+
text: :class:`str`
|
|
45
|
+
The key to find in the localization file.
|
|
46
|
+
locale: Union[:class:`str` | :class:`discord.Locale` | :class:`discord.Guild` | :class:`discord.Interaction` | :class:`discord.commands.ext.Context`]
|
|
47
|
+
The locale to find the localization with, or an object that has an attribute that returns :class:`discord.Locale`.
|
|
48
|
+
**kwargs: :class:`Any`
|
|
49
|
+
The arguments to pass to the string formatter.
|
|
50
|
+
|
|
51
|
+
-------
|
|
52
|
+
### Returns:
|
|
53
|
+
:class:`str`
|
|
54
|
+
The localized string.
|
|
55
|
+
List[:class:`str`]
|
|
56
|
+
If the localization is a list, it returns the list of localized strings.
|
|
57
|
+
"""
|
|
58
|
+
if isinstance(locale, Guild):
|
|
59
|
+
locale = str(locale.preferred_locale)
|
|
60
|
+
elif isinstance(locale, (Interaction, Context)):
|
|
61
|
+
locale = str(locale.guild.preferred_locale)
|
|
62
|
+
elif isinstance(locale, (Locale, str)):
|
|
63
|
+
locale = str(locale)
|
|
64
|
+
else:
|
|
65
|
+
raise TypeError("locale must be of type str, discord.Locale, discord.Guild, discord.Interaction, or discord.ext.commands.Context, received {}".format(type(locale)))
|
|
66
|
+
|
|
67
|
+
localizations = self.file.get(locale) or self.file.get(self.default_locale)
|
|
68
|
+
if not localizations:
|
|
69
|
+
if self.error:
|
|
70
|
+
raise KeyError("no localizations for language {}".format(locale))
|
|
71
|
+
else:
|
|
72
|
+
logging.error("No localizations for language {}".format(locale))
|
|
73
|
+
return text
|
|
74
|
+
|
|
75
|
+
localized_text = localizations.get(text)
|
|
76
|
+
if localized_text:
|
|
77
|
+
if isinstance(localized_text, list):
|
|
78
|
+
return [localized_item.format(**kwargs) for localized_item in localizations[text]]
|
|
79
|
+
return localized_text.format(**kwargs)
|
|
80
|
+
else:
|
|
81
|
+
if self.error:
|
|
82
|
+
raise KeyError("localization \"{}\" not found for language {}".format(text, locale))
|
|
83
|
+
else:
|
|
84
|
+
logging.error("Localization \"{}\" not found for language {}".format(text, locale))
|
|
85
|
+
return text
|
|
86
|
+
|
|
87
|
+
_ = t = translate = localise = localize
|
|
88
|
+
|
|
89
|
+
def one(self, text: str, number: Union[int, float], locale: str, **kwargs: Any) -> str:
|
|
90
|
+
"""
|
|
91
|
+
Gets the singular and plural form of a string like it's done in i18n.
|
|
92
|
+
|
|
93
|
+
For this, you need to have the key in the JSON be a list of two strings, the first being the singular form, the second being the plural form.
|
|
94
|
+
|
|
95
|
+
-------
|
|
96
|
+
### Parameters:
|
|
97
|
+
text: `str`
|
|
98
|
+
The key to find in the localization file.
|
|
99
|
+
locale: Union[:class:`str` | :class:`discord.Locale` | :class:`discord.Guild` | :class:`discord.Interaction` | :class:`discord.commands.ext.Context`]
|
|
100
|
+
The locale to find the localization with, or an object that has an attribute that returns :class:`discord.Locale`.
|
|
101
|
+
number: Union[`int`, `float`]
|
|
102
|
+
The number to determine whether to use the singular or plural form.
|
|
103
|
+
**kwargs: `Any`
|
|
104
|
+
The arguments to pass to the string formatter.
|
|
105
|
+
|
|
106
|
+
-------
|
|
107
|
+
### Returns:
|
|
108
|
+
`str` If `num` is 1, returns the first item of the list. Otherwise, it returns the last item of the list. If the list only has 1 item, it returns that item.
|
|
109
|
+
"""
|
|
110
|
+
if isinstance(locale, Guild):
|
|
111
|
+
locale = str(locale.preferred_locale)
|
|
112
|
+
elif isinstance(locale, (Interaction, Context)):
|
|
113
|
+
locale = str(locale.guild.preferred_locale)
|
|
114
|
+
elif isinstance(locale, (Locale, str)):
|
|
115
|
+
locale = str(locale)
|
|
116
|
+
else:
|
|
117
|
+
raise TypeError("locale must be of type str, discord.Locale, discord.Guild, discord.Interaction, or discord.ext.commands.Context, received {}".format(type(locale)))
|
|
118
|
+
|
|
119
|
+
localized_text = self.localize(text, locale, **kwargs)
|
|
120
|
+
if not isinstance(localized_text, list):
|
|
121
|
+
raise TypeError("translation for \"{}\" is not a list".format(text))
|
|
122
|
+
|
|
123
|
+
if not localized_text:
|
|
124
|
+
if self.error:
|
|
125
|
+
raise KeyError("localization \"{}\" not found for language {}".format(text, locale))
|
|
126
|
+
else:
|
|
127
|
+
logging.error("Localization \"{}\" not found for language {}".format(text, locale))
|
|
128
|
+
return text
|
|
129
|
+
|
|
130
|
+
if number == 1:
|
|
131
|
+
return localized_text[0]
|
|
132
|
+
else:
|
|
133
|
+
return localized_text[-1]
|
|
134
|
+
|
|
135
|
+
_o = o = one
|
|
136
|
+
|
|
137
|
+
__call__ = localize
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: discord-localization
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: A discord.py extension for command localization.
|
|
5
|
+
Author: Pearoo
|
|
6
|
+
Author-email: <contact@pearoo.dev>
|
|
7
|
+
Project-URL: Source, https://github.com/PearooXD/discord-localization
|
|
8
|
+
Keywords: discord.py,i18n,translation,localisation,localization
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
|
|
15
|
+
[Github](https://github.com/PearooXD/discord-localization) | [PyPI](https://pypi.org/project/discord-localization/)
|
|
16
|
+
|
|
17
|
+
--------
|
|
18
|
+
## Setting up
|
|
19
|
+
Start by downloading the package via pip.
|
|
20
|
+
```
|
|
21
|
+
python3 -m pip install discord-localization
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
After that, you can just import it into your file as a discord.py extension.
|
|
25
|
+
|
|
26
|
+
--------
|
|
27
|
+
## Examples
|
|
28
|
+
```py
|
|
29
|
+
from discord.ext import localization
|
|
30
|
+
import random
|
|
31
|
+
|
|
32
|
+
_ = localization.Localization("main.i18n.json", "en") # the first option is the relative path to the localization file. the second is the fallback/default language that is used if the actual language is not found.
|
|
33
|
+
dice = random.randint(1, 6)
|
|
34
|
+
language = input("What's your native language? / Was ist Deine Muttersprache? (en/de) >>> ")
|
|
35
|
+
|
|
36
|
+
print(_("dice_result", language, dice=dice))
|
|
37
|
+
```
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"en": {
|
|
41
|
+
"dice_result": "Your dice rolled {dice}!"
|
|
42
|
+
},
|
|
43
|
+
"de": {
|
|
44
|
+
"dice_result": "Dein Würfel hat eine {dice} gewürfelt!"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Integrating it into your Discord bot
|
|
50
|
+
```py
|
|
51
|
+
import discord
|
|
52
|
+
from discord.ext import commands, localization
|
|
53
|
+
|
|
54
|
+
bot = commands.Bot(command_prefix="!", intents=discord.Intents.all()) # hey guys, breaking the 4th wall here. please stop using Intents.default() just to set the message_content intent to True right after. Intents.all() does it for you.
|
|
55
|
+
_ = localization.Localization("main_localization.json", "en")
|
|
56
|
+
|
|
57
|
+
@bot.event
|
|
58
|
+
async def on_ready():
|
|
59
|
+
print("Bot is ready!")
|
|
60
|
+
|
|
61
|
+
@bot.command()
|
|
62
|
+
async def ping(ctx):
|
|
63
|
+
await ctx.reply(_("ping", ctx, latency=bot.latency * 1000))
|
|
64
|
+
```
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"en": {
|
|
68
|
+
"ping": "Pong! {latency}ms"
|
|
69
|
+
},
|
|
70
|
+
"en-US": {
|
|
71
|
+
"ping": "Pong! {latency}ms, but American! (because Discord makes a difference between en-US and en-UK locales - you can circumvent this by setting the default_locale)"
|
|
72
|
+
},
|
|
73
|
+
"en-UK": {
|
|
74
|
+
"ping": "Ping is {latency}ms, innit, bruv? (i'm sorry)"
|
|
75
|
+
},
|
|
76
|
+
"fr": {
|
|
77
|
+
"ping": "Bonjour! Ping is {latency}ms!"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
Explanation:
|
|
82
|
+
- By setting the default locale to "en", we won't have any issues with unfinished localizations. This way, we are also not required to define localizations for both "en-US" and "en-UK" (since Discord's API differentiates them, however, usually, bots don't.)
|
|
83
|
+
- We are passing a `latency` argument to the localization function (which is also available with `Localization._`! But calling the object is fine too.), which will be later used whenever we use `{latency}` in a localization.
|
|
84
|
+
- We are defining an `en` locale in the JSON file, even though both Discord's API and discord.py doesn't have such a locale. This is to make it more obvious that everything in that locale should be in English - you could also name it `default_locale`, you just have to make sure to edit it in the `Localization` object.
|
|
85
|
+
- We pass `ctx` to the localization function, which will automatically try getting `ctx.guild.preferred_locale`. This works with `Interaction` (by using `Interaction.guild.preferred_locale` internally), `Guild` (by using `guild.preferred_locale` internally), or just passing the `Locale` itself, for example, `discord.Locale.american_english` (which will convert to the `en-US` locale).
|
|
86
|
+
|
|
87
|
+
-------
|
|
88
|
+
## Working with plurals
|
|
89
|
+
```py
|
|
90
|
+
from discord.ext.localization import Localization
|
|
91
|
+
import locale
|
|
92
|
+
|
|
93
|
+
_ = Localization("main_plurals.json") # this will look for main_plurals.json as the language JSON file
|
|
94
|
+
apples = int(input("How many apples do you have? >>> "))
|
|
95
|
+
language = locale.getlocale()[0][:2] # this will return the default OS language, such as en, hu, de, fr, es etc.
|
|
96
|
+
|
|
97
|
+
print(_.one("apples", apples, language, apples=apples))
|
|
98
|
+
```
|
|
99
|
+
```json
|
|
100
|
+
{
|
|
101
|
+
"en": {
|
|
102
|
+
"apples": ["You only have one apple! What a loser...", "You have {apples} apples!"]
|
|
103
|
+
},
|
|
104
|
+
"hu": {
|
|
105
|
+
"apples": ["{apples} almád van!"]
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
This example does a great job at presenting how the `.one()` function works. Here's the explanation for the code:
|
|
111
|
+
|
|
112
|
+
The code will look for the default OS language, and will tell you how many apples you have with plurals in mind. I always get angry when I see "you have 1 apples"-ish text on a website, and it's an easy fix, but for some reason, many developers don't pay attention to it.
|
|
113
|
+
|
|
114
|
+
Hungarian doesn't make a difference between plurals, so the list for "hu"/"apples" only has 1 item. This won't be a problem though - the library isn't hardcoded to only look for the first and second elements in the returned list, it will look for the **first** and the **last**. Which means that if a list only has 1 item, it will only return that item.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
setup.py
|
|
4
|
+
discord/ext/localization/__init__.py
|
|
5
|
+
discord/ext/localization/localization.py
|
|
6
|
+
discord_localization.egg-info/PKG-INFO
|
|
7
|
+
discord_localization.egg-info/SOURCES.txt
|
|
8
|
+
discord_localization.egg-info/dependency_links.txt
|
|
9
|
+
discord_localization.egg-info/top_level.txt
|
|
10
|
+
tests/test.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
discord
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from setuptools import setup
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
VERSION = '0.1.2'
|
|
6
|
+
DESCRIPTION = 'A discord.py extension for command localization.'
|
|
7
|
+
|
|
8
|
+
# Setting up
|
|
9
|
+
setup(
|
|
10
|
+
name="discord-localization",
|
|
11
|
+
version=VERSION,
|
|
12
|
+
author="Pearoo",
|
|
13
|
+
author_email="<contact@pearoo.dev>",
|
|
14
|
+
description=DESCRIPTION,
|
|
15
|
+
long_description_content_type="text/markdown",
|
|
16
|
+
long_description=Path("README.md").read_text(),
|
|
17
|
+
packages=["discord.ext.localization"],
|
|
18
|
+
install_requires=[],
|
|
19
|
+
keywords=['discord.py', 'i18n', 'translation', 'localisation', 'localization'],
|
|
20
|
+
classifiers=[
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"License :: OSI Approved :: MIT License",
|
|
23
|
+
"Operating System :: OS Independent"
|
|
24
|
+
],
|
|
25
|
+
project_urls={
|
|
26
|
+
"Source": "https://github.com/PearooXD/discord-localization"
|
|
27
|
+
}
|
|
28
|
+
)
|
|
29
|
+
# command: python setup.py sdist bdist_wheel
|