feat: Load language file in lang/ instead of the ini file.

This commit is contained in:
Terry Geng 2020-07-11 11:01:33 +08:00
parent e84607b8e8
commit 9e2e09e5fd
No known key found for this signature in database
GPG Key ID: F982F8EA1DF720E7
12 changed files with 123 additions and 217 deletions

View File

@ -20,6 +20,7 @@ steps:
- echo "current git commit is $version" - echo "current git commit is $version"
- echo $version > /mnt/botamusique/testing-version - echo $version > /mnt/botamusique/testing-version
- sed -i "s/version = 'git'/version = '$version'/" mumbleBot.py - sed -i "s/version = 'git'/version = '$version'/" mumbleBot.py
- ./lang/translate.py
- rm -rf .git* - rm -rf .git*
- rm -rf web - rm -rf web
- mkdir /tmp/botamusique - mkdir /tmp/botamusique

View File

@ -6,7 +6,8 @@ import json
import re import re
import pymumble_py3 as pymumble import pymumble_py3 as pymumble
from constants import tr, commands from constants import tr_cli as tr
from constants import commands
import interface import interface
import media.system import media.system
import util import util

View File

@ -195,159 +195,3 @@ list_webinterface_user = webuserlist
add_webinterface_user = webuseradd add_webinterface_user = webuseradd
remove_webinterface_user = webuserdel remove_webinterface_user = webuserdel
change_user_password = password change_user_password = password
[strings]
current_volume = Current volume: {volume}.
current_ducking_volume = Volume on ducking: {volume}.
change_volume = Volume set to {volume} by {user}.
change_ducking_volume = Volume on ducking set to {volume} by {user}.
bad_command = {command}: command not found.
bad_parameter = {command}: invalid parameter.
error_executing_command = {command}: Command failed with error: {error}.
not_admin = You are not an admin!
not_playing = Nothing is playing right now.
no_file = File not found.
wrong_pattern = Invalid regex: {error}.
file_added = Added {item}.
file_deleted = Deleted {item} from the library.
multiple_file_added = Multiple items added:
multiple_file_deleted = Multiple items deleted from the library:
multiple_file_found = Found:
page_instruction = Page {current}/{total}. Use <i>!{command} {{page}}</i> to navigate.
records_omitted = ...
bad_url = Bad URL requested.
preconfigurated_radio = Preconfigurated Radio available:
unable_download = Unable to download <b>{item}</b>. Removed from the library.
unable_play = Unable to play <b>{item}</b>. Removed from the library.
which_command = Do you mean <br /> {commands}
multiple_matches = File not found! Possible candidates:
queue_contents = Items on the playlist:
queue_empty = Playlist is empty!
invalid_index = Invalid index <i>{index}</i>. Use '!queue' to see your playlist.
now_playing = Playing {item}
radio = Radio
file = File
url_from_playlist = URL
url = URL
radio_item = <a href="{url}"><b>{title}</b></a> <i>from</i> {name} <i>added by</i> {user}
file_item = <b>{artist} - {title}</b> <i>added by</i> {user}
url_from_playlist_item = <a href="{url}"><b>{title}</b></a> <i>from playlist</i> <a href="{playlist_url}">{playlist}</a> <i>added by</i> {user}
url_item = <a href="{url}"><b>{title}</b></a> <i>added by</i> {user}
not_in_my_channel = You're not in my channel, command refused!
pm_not_allowed = Private message aren't allowed.
too_long = <b>{song}</b> is too long, removed from playlist!
download_in_progress = Download of <b>{item}</b> in progress...
removing_item = Removed entry {item} from playlist.
user_ban = You are banned, not allowed to do that!
url_ban = This url is banned!
rb_query_result = This is the result of your query, send !rbplay 'ID' to play a station:
rb_play_empty = Please specify a radio station ID!
paused = Music paused.
stopped = Music stopped.
cleared = Playlist emptied.
database_dropped = Database dropped. All records have gone.
new_version_found = <h2>Update Available!</h2> Version {new_version} of botamusique is available! <hr />
<h3>Changelog</h3> {changelog} <hr /> Send <i>!update</i> to update!
update_successful = <h2>botamusique v{version} Installed!</h2><hr />
<h3>Changelog</h3> {changelog} <hr /> Visit <a href="https://github.com/azlux/botamusique">our github repo</a> for more details!
start_updating = Start updating...
file_missed = Music file '{file}' missed! This item has been removed from the playlist.
unknown_mode = Unknown playback mode '{mode}'. It should be one of <i>one-shot</i>, <i>repeat</i>, <i>random</i>.
current_mode = Current playback mode is <i>{mode}</i>.
change_mode = Playback mode set to <i>{mode}</i> by {user}.
repeat = Repeat {song} for {n} times.
yt_result = Youtube query result: {result_table} Use <i>!sl {{indexes}}</i> to play the item you want. <br />
<i>!ytquery -n</i> for the next page.
yt_no_more = No more results!
yt_query_error = Unable to query youtube!
playlist_fetching_failed = Unable to fetch the playlist!
cache_refreshed = Cache refreshed!
added_tags = Added tags <i>{tags}</i> to <b>{song}</b>.
added_tags_to_all = Added tags <i>{tags}</i> to songs on the playlist.
removed_tags = Removed tags <i>{tags}</i> from <b>{song}</b>.
removed_tags_from_all = Removed tags <i>{tags}</i> from songs on the playlist.
cleared_tags = Removed all tags from <b>{song}</b>.
cleared_tags_from_all = Removed all tags from songs on the playlist.
shortlist_instruction = Use <i>!sl {indexes}</i> to play the item you want.
auto_paused = Use <i>!play</i> to resume music!
webpage_address= Your own address to access the web interface is <a href="{address}">{address}</a>
web_user_list = Following users has the privilege to access the web interface: <br /> {users}
user_password_set = Your password has been updated.
command_disabled = {command}: command disabled!
help = <h3>Commands</h3>
<b>Control</b>
<ul>
<li> <b>!<u>w</u>eb</b> - get the URL of the web interface, if enabled. </li>
<li> <b>!play </b> (or <b>!p</b>) [{num}] [{start_from}] - resume from pausing / start to play (the num-th song is num if given) </li>
<li> <b>!<u>pa</u>use </b> - pause </li>
<li> <b>!<u>st</u>op </b> - stop playing </li>
<li> <b>!<u>sk</u>ip </b> - jump to the next song </li>
<li> <b>!<u>la</u>st </b> - jump to the last song </li>
<li> <b>!<u>v</u>olume </b> {volume} - get or change the volume (from 0 to 100) </li>
<li> <b>!<u>m</u>ode </b> [{mode}] - get or set the playback mode, {mode} should be one of <i>one-shot</i> (remove
item once played), <i>repeat</i> (looping through the playlist), <i>random</i> (randomize the playlist),
<i>autoplay</i> (randomly grab something from the music library).</li>
<li> <b>!duck </b> on/off - enable or disable ducking function </li>
<li> <b>!duckv </b> - set the volume of the bot when ducking is activated </li>
<li> <b>!<u>duckt</u>hres </b> - set the threshold of volume to activate ducking (3000 by default) </li>
<li> <b>!<u>o</u>ust </b> - stop playing and go to default channel </li>
</ul>
<b>Playlist</b>
<ul>
<li> <b>!<u>n</u>ow </b> (or <b>!np</b>) - display the current song </li>
<li> <b>!<u>q</u>ueue </b> - display items in the playlist </li>
<li> <b>!<u>t</u>ag </b> {tags} - add all items with tags {tags}, tags separated by ",". </li>
<li> <b>!file </b>(or <b>!f</b>) {path/folder/keyword} - add a single file to the playlist by its path or keyword in its path. </li>
<li> <b>!<u>filem</u>atch </b>(or <b>!fm</b>) {pattern} - add all files that match regex {pattern} </li>
<li> <b>!<u>ur</u>l </b> {url} - add Youtube or SoundCloud music </li>
<li> <b>!<u>playl</u>ist </b> {url} [{offset}] - add all items in a Youtube or SoundCloud playlist, and start with the {offset}-th item </li>
<li> <b>!<u>rad</u>io </b> {url} - append a radio {url} to the playlist </li>
<li> <b>!<u>rbq</u>uery </b> {keyword} - query http://www.radio-browser.info for a radio station </li>
<li> <b>!<u>rbp</u>lay </b> {id} - play a radio station with {id} (eg. !rbplay 96746) </li>
<li> <b>!<u>ys</u>earch </b> {keywords} - query youtube. Use <i>!ysearch -n</i> to turn the page. </li>
<li> <b>!<u>yp</u>lay </b> {keywords} - add the first search result of {keywords} into the playlist.</li>
<li> <b>!<u>sh</u>ortlist </b> (or <b>!sl</b>) {indexes/*} - add {indexes}-th item (or all items if * is given) on the shortlist. </li>
<li> <b>!rm </b> {num} - remove the num-th song on the playlist </li>
<li> <b>!<u>rep</u>eat </b> [{num}] - repeat current song {num} (1 by default) times.</li>
<li> <b>!<u>ran</u>dom </b> - randomize the playlist.</li>
</ul>
<b>Music Library</b>
<ul>
<li> <b>!<u>se</u>arch </b> {keywords} - find item with {keywords} in the music library, keywords separated by space.</li>
<li> <b>!<u>li</u>stfile </b> [{pattern}] - display list of available files (whose paths match the regex pattern if {pattern} is given) </li>
<li> <b>!<u>addt</u>ag </b> [{index}] {tags} - add {tags} to {index}-th(current song if {index} is omitted) item on the playlist, tags separated by ",". </li>
<li> <b>!<u>addt</u>ag </b> * {tags} - add {tags} to all items on the playlist. </li>
<li> <b>!<u>un</u>tag </b> [{index/*}] {tags}/* - remove {tags}/all tags from {index}-th(current song if {index} is omitted) item on the playlist. </li>
<li> <b>!<u>fin</u>dtagged </b> (or <b>!ft</b>) {tags} - find item with {tags} in the music library. </li>
<li> <b>!<u>del</u>ete </b> {index} - delete {index}-th item on the shortlist from the music library. </li>
</ul>
<b>Other</b>
<ul>
<li> <b>!<u>j</u>oinme {token} </b> - join your own channel with {token}.</li>
<li> <b>!<u>password</u> {password} </b> - change your password, used to access the web interface.</li>
</ul>
admin_help = <h3>Admin command</h3>
<b>Bot</b>
<ul>
<li><b>!<u>k</u>ill </b> - kill the bot</li>
<li><b>!update </b> - update the bot</li>
<li><b>!userban </b> {user} - ban a user</li>
<li><b>!userunban </b> {user} - unban a user</li>
<li><b>!urlbanlist </b> - list banned url</li>
<li><b>!urlban </b> [{url}] - ban {url} (or current item's url by default) and remove this url from the library.</li>
<li><b>!urlunban </b> {url} - unban {url}</li>
<li><b>!rescan </b> {url} - rebuild local music file cache</li>
<li><b>!dropdatabase</b> - clear the entire database, you will lose all settings and music library.</li>
</ul>
<b>Web Interface</b>
<ul>
<li><b>!<u>webuserlist</u></b> - list all users that have the permission of accessing the web interface, if auth mode is 'password'.</li>
<li><b>!<u>webuseradd</u> {nick name}</b> - grant the user with {nick name} the access to the web interface, if auth mode is 'password'.</li>
<li><b>!<u>webuserdel</u> {nick name}</b> - revoke the access to the web interface of {nick name}, if auth mode is 'password'.</li>
<li><b>!update </b> - update the bot</li>
<li><b>!userban </b> {user} - ban a user</li>
</ul>

View File

@ -177,18 +177,3 @@ port = 64738
# see configuration.default.ini. Copy options you want to edit into this file. # see configuration.default.ini. Copy options you want to edit into this file.
#play_file = file, f #play_file = file, f
#play_file_match = filematch, fm #play_file_match = filematch, fm
# [strings] is used to compose what the bot says. You can customize them to fit in
# the style of your channel, or translate into your own language.
# For a full list of strings, please see configuration.default.ini.
# Copy options you want to edit into this file.
# Note: please keep those {placeholder} of each string in your new string.
[strings]
# Some examples are:
#current_volume = Current volume: {volume}
#current_volume = 当前音量为{volume}
#current_volume = よく聞いてね!今の音量は{volume}<br />ちゃんと覚える:大音量で耳が悪くなる!
#
#bad_command = {command}: command not found
#bad_command = {command}: 未知命令,键入'!help'以获取可用命令列表
#bad_command = {command}がなに?食べれる?おいしいでしか?

View File

@ -1,24 +1,45 @@
import json
import variables as var import variables as var
lang_dict = {}
def tr(option, *argv, **kwargs):
def load_lang(lang):
global lang_dict
with open(f"lang/{lang}", "r") as f:
lang_dict = json.load(f)
def tr_cli(option, *argv, **kwargs):
try: try:
string = var.config.get("strings", option) string = lang_dict['cli'][option]
except KeyError: except KeyError:
raise KeyError("Missed strings in configuration file: '{string}'. ".format(string=option) raise KeyError("Missed strings in language file: '{string}'. ".format(string=option))
+ "Please restore you configuration file back to default if necessary.") return _tr(string, *argv, **kwargs)
def tr_web(option, *argv, **kwargs):
try:
string = lang_dict['web'][option]
except KeyError:
raise KeyError("Missed strings in language file: '{string}'. ".format(string=option))
return _tr(string, *argv, **kwargs)
def _tr(string, *argv, **kwargs):
if argv or kwargs: if argv or kwargs:
try: try:
formatted = string.format(*argv, **kwargs) formatted = string.format(*argv, **kwargs)
return formatted return formatted
except KeyError as e: except KeyError as e:
raise KeyError( raise KeyError(
"Missed/Unexpected placeholder {{{placeholder}}} in string '{string}'. ".format(placeholder=str(e).strip("'"), string=option) "Missed/Unexpected placeholder {{{placeholder}}} in string "
+ "Please restore you configuration file back to default if necessary.") "'{string}'. ".format(placeholder=str(e).strip("'"),
string=string))
except TypeError: except TypeError:
raise KeyError( raise KeyError(
"Missed placeholder in string '{string}'. ".format(string=option) "Missed placeholder in string '{string}'. ".format(string=string))
+ "Please restore you configuration file back to default if necessary.")
else: else:
return string return string
@ -28,5 +49,4 @@ def commands(command):
string = var.config.get("commands", command) string = var.config.get("commands", command)
return string return string
except KeyError: except KeyError:
raise KeyError("Missed command in configuration file: '{string}'. ".format(string=command) raise KeyError("Missed command in configuration file: '{string}'. ".format(string=command))
+ "Please restore you configuration file back to default if necessary.")

28
lang/translate.py Normal file
View File

@ -0,0 +1,28 @@
#!/usr/bin/env python3
import requests
base_url = "https://translate.azlux.fr/api/v1"
client = "be8215d4-2417-49db-9355-c418f26dc3f4"
secret = "MIMvdnECLkmTZyCQT4DekONN53EOSsj3"
project_id = "4aafb197-3282-47b3-a197-0ca870cf6ab2"
data = {"grant_type": "client_credentials",
"client_id": client,
"client_secret": secret}
r = requests.post(f"{base_url}/auth/token", json=data)
token = r.json()["access_token"]
headers = {"Authorization": "Bearer " + token,
"Accept": "application/json, text/plain, */*"}
r = requests.get(f"{base_url}/projects/{project_id}/translations", headers=headers)
translations = r.json()['data']
for translation in translations:
lang_code = translation['locale']['code']
params = {'locale': lang_code,
'format': 'jsonnested'}
r = requests.get(f"{base_url}/projects/{project_id}/exports", params=params, headers=headers)
with open(lang_code, "wb")as f:
f.write(r.content)

View File

@ -9,7 +9,7 @@ from PIL import Image
import util import util
import variables as var import variables as var
from media.item import BaseItem, item_builders, item_loaders, item_id_generators, ValidationFailedError from media.item import BaseItem, item_builders, item_loaders, item_id_generators, ValidationFailedError
import constants from constants import tr_cli as tr
''' '''
type : file type : file
@ -75,7 +75,7 @@ class FileItem(BaseItem):
if not os.path.exists(self.uri()): if not os.path.exists(self.uri()):
self.log.info( self.log.info(
"file: music file missed for %s" % self.format_debug_string()) "file: music file missed for %s" % self.format_debug_string())
raise ValidationFailedError(constants.tr('file_missed', file=self.path)) raise ValidationFailedError(tr('file_missed', file=self.path))
if self.duration == 0: if self.duration == 0:
self.duration = util.get_media_duration(self.uri()) self.duration = util.get_media_duration(self.uri())
@ -185,14 +185,14 @@ class FileItem(BaseItem):
) )
def format_song_string(self, user): def format_song_string(self, user):
return constants.tr("file_item", return tr("file_item",
title=self.title, title=self.title,
artist=self.artist if self.artist else '??', artist=self.artist if self.artist else '??',
user=user user=user
) )
def format_current_playing(self, user): def format_current_playing(self, user):
display = constants.tr("now_playing", item=self.format_song_string(user)) display = tr("now_playing", item=self.format_song_string(user))
if self.thumbnail: if self.thumbnail:
thumbnail_html = '<img width="80" src="data:image/jpge;base64,' + \ thumbnail_html = '<img width="80" src="data:image/jpge;base64,' + \
self.thumbnail + '"/>' self.thumbnail + '"/>'
@ -208,4 +208,4 @@ class FileItem(BaseItem):
return title return title
def display_type(self): def display_type(self):
return constants.tr("file") return tr("file")

View File

@ -7,7 +7,7 @@ import hashlib
from media.item import BaseItem from media.item import BaseItem
from media.item import item_builders, item_loaders, item_id_generators from media.item import item_builders, item_loaders, item_id_generators
import constants from constants import tr_cli as tr
log = logging.getLogger("bot") log = logging.getLogger("bot")
@ -147,18 +147,18 @@ class RadioItem(BaseItem):
) )
def format_song_string(self, user): def format_song_string(self, user):
return constants.tr("radio_item", return tr("radio_item",
url=self.url, url=self.url,
title=get_radio_title(self.url), # the title of current song title=get_radio_title(self.url), # the title of current song
name=self.title, # the title of radio station name=self.title, # the title of radio station
user=user user=user
) )
def format_current_playing(self, user): def format_current_playing(self, user):
return constants.tr("now_playing", item=self.format_song_string(user)) return tr("now_playing", item=self.format_song_string(user))
def format_title(self): def format_title(self):
return self.title if self.title else self.url return self.title if self.title else self.url
def display_type(self): def display_type(self):
return constants.tr("radio") return tr("radio")

View File

@ -9,7 +9,7 @@ import glob
from io import BytesIO from io import BytesIO
import base64 import base64
import constants from constants import tr_cli as tr
import media import media
import variables as var import variables as var
from media.item import BaseItem, item_builders, item_loaders, item_id_generators, ValidationFailedError, \ from media.item import BaseItem, item_builders, item_loaders, item_id_generators, ValidationFailedError, \
@ -99,7 +99,7 @@ class URLItem(BaseItem):
# Check the length, useful in case of playlist, it wasn't checked before) # Check the length, useful in case of playlist, it wasn't checked before)
log.info( log.info(
"url: " + self.url + " has a duration of " + str(self.duration / 60) + " min -- too long") "url: " + self.url + " has a duration of " + str(self.duration / 60) + " min -- too long")
raise ValidationFailedError(constants.tr('too_long', song=self.title)) raise ValidationFailedError(tr('too_long', song=self.title))
else: else:
self.ready = "validated" self.ready = "validated"
self.version += 1 # notify wrapper to save me self.version += 1 # notify wrapper to save me
@ -138,7 +138,7 @@ class URLItem(BaseItem):
if not succeed: if not succeed:
self.ready = 'failed' self.ready = 'failed'
self.log.error("url: error while fetching info from the URL") self.log.error("url: error while fetching info from the URL")
raise ValidationFailedError(constants.tr('unable_download', item=self.format_title())) raise ValidationFailedError(tr('unable_download', item=self.format_title()))
def _download(self): def _download(self):
media.system.clear_tmp_folder(var.tmp_folder, var.config.getint('bot', 'tmp_folder_max_size')) media.system.clear_tmp_folder(var.tmp_folder, var.config.getint('bot', 'tmp_folder_max_size'))
@ -193,7 +193,7 @@ class URLItem(BaseItem):
os.remove(f) os.remove(f)
self.ready = "failed" self.ready = "failed"
self.downloading = False self.downloading = False
raise PreparationFailedError(constants.tr('unable_download', item=self.format_title())) raise PreparationFailedError(tr('unable_download', item=self.format_title()))
def _read_thumbnail_from_file(self, path_thumbnail): def _read_thumbnail_from_file(self, path_thumbnail):
if os.path.isfile(path_thumbnail): if os.path.isfile(path_thumbnail):
@ -226,14 +226,14 @@ class URLItem(BaseItem):
def format_song_string(self, user): def format_song_string(self, user):
if self.ready in ['validated', 'yes']: if self.ready in ['validated', 'yes']:
return constants.tr("url_item", return tr("url_item",
title=self.title if self.title else "??", title=self.title if self.title else "??",
url=self.url, url=self.url,
user=user) user=user)
return self.url return self.url
def format_current_playing(self, user): def format_current_playing(self, user):
display = constants.tr("now_playing", item=self.format_song_string(user)) display = tr("now_playing", item=self.format_song_string(user))
if self.thumbnail: if self.thumbnail:
thumbnail_html = '<img width="80" src="data:image/jpge;base64,' + \ thumbnail_html = '<img width="80" src="data:image/jpge;base64,' + \
@ -246,4 +246,4 @@ class URLItem(BaseItem):
return self.title if self.title.strip() else self.url return self.title if self.title.strip() else self.url
def display_type(self): def display_type(self):
return constants.tr("url") return tr("url")

View File

@ -1,5 +1,5 @@
import youtube_dl import youtube_dl
import constants from constants import tr_cli as tr
import variables as var import variables as var
from media.item import item_builders, item_loaders, item_id_generators from media.item import item_builders, item_loaders, item_id_generators
from media.url import URLItem, url_item_id_generator from media.url import URLItem, url_item_id_generator
@ -96,7 +96,7 @@ class PlaylistURLItem(URLItem):
) )
def format_song_string(self, user): def format_song_string(self, user):
return constants.tr("url_from_playlist_item", return tr("url_from_playlist_item",
title=self.title, title=self.title,
url=self.url, url=self.url,
playlist_url=self.playlist_url, playlist_url=self.playlist_url,
@ -104,7 +104,7 @@ class PlaylistURLItem(URLItem):
user=user) user=user)
def format_current_playing(self, user): def format_current_playing(self, user):
display = constants.tr("now_playing", item=self.format_song_string(user)) display = tr("now_playing", item=self.format_song_string(user))
if self.thumbnail: if self.thumbnail:
thumbnail_html = '<img width="80" src="data:image/jpge;base64,' + \ thumbnail_html = '<img width="80" src="data:image/jpge;base64,' + \
@ -114,4 +114,4 @@ class PlaylistURLItem(URLItem):
return display return display
def display_type(self): def display_type(self):
return constants.tr("url_from_playlist") return tr("url_from_playlist")

View File

@ -23,7 +23,8 @@ from packaging import version
import util import util
import command import command
from constants import tr, commands import constants
from constants import tr_cli as tr
from database import SettingsDatabase, MusicDatabase, DatabaseMigration from database import SettingsDatabase, MusicDatabase, DatabaseMigration
import media.system import media.system
from media.item import ValidationFailedError, PreparationFailedError from media.item import ValidationFailedError, PreparationFailedError
@ -713,6 +714,8 @@ def start_web_interface(addr, port):
if __name__ == '__main__': if __name__ == '__main__':
supported_languages = util.get_supported_language()
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description='Bot for playing music on Mumble') description='Bot for playing music on Mumble')
@ -720,11 +723,11 @@ if __name__ == '__main__':
parser.add_argument("--config", dest='config', type=str, default='configuration.ini', parser.add_argument("--config", dest='config', type=str, default='configuration.ini',
help='Load configuration from this file. Default: configuration.ini') help='Load configuration from this file. Default: configuration.ini')
parser.add_argument("--db", dest='db', type=str, parser.add_argument("--db", dest='db', type=str,
default=None, help='Settings database file. Default: settings-{username_of_the_bot}.db') default=None, help='Settings database file')
parser.add_argument("--music-db", dest='music_db', type=str, parser.add_argument("--music-db", dest='music_db', type=str,
default=None, help='Music library database file. Default: music.db') default=None, help='Music library database file')
parser.add_argument("--lang", dest='lang', type=str, default='en_US', parser.add_argument("--lang", dest='lang', type=str, default=None,
help='Preferred language.') help='Preferred language. Support ' + ", ".join(supported_languages))
parser.add_argument("-q", "--quiet", dest="quiet", parser.add_argument("-q", "--quiet", dest="quiet",
action="store_true", help="Only Error logs") action="store_true", help="Only Error logs")
@ -817,6 +820,20 @@ if __name__ == '__main__':
var.music_folder = util.solve_filepath(var.config.get('bot', 'music_folder')) var.music_folder = util.solve_filepath(var.config.get('bot', 'music_folder'))
var.tmp_folder = util.solve_filepath(var.config.get('bot', 'tmp_folder')) var.tmp_folder = util.solve_filepath(var.config.get('bot', 'tmp_folder'))
# ======================
# Translation
# ======================
lang = ""
if args.lang:
lang = args.lang
else:
lang = var.config.get('bot', 'language', fallback='en_US')
if lang not in supported_languages:
raise KeyError(f"Unsupported language {lang}")
constants.load_lang(lang)
# ====================== # ======================
# Prepare Cache # Prepare Cache
# ====================== # ======================

12
util.py
View File

@ -8,7 +8,6 @@ import io
import sys import sys
import variables as var import variables as var
import zipfile import zipfile
import requests
import re import re
import subprocess as sp import subprocess as sp
import logging import logging
@ -416,6 +415,17 @@ def verify_password(password, salted_hash, salt):
return False return False
def get_supported_language():
lang_files = os.listdir('lang')
lang_list = []
for lang_file in lang_files:
match = re.search("[a-z]{2}_[A-Z]{2}", lang_file)
if match:
lang_list.append(lang_file)
return lang_list
class LoggerIOWrapper(io.TextIOWrapper): class LoggerIOWrapper(io.TextIOWrapper):
def __init__(self, logger: logging.Logger, logging_level, fallback_io_buffer): def __init__(self, logger: logging.Logger, logging_level, fallback_io_buffer):
super().__init__(fallback_io_buffer, write_through=True) super().__init__(fallback_io_buffer, write_through=True)