diff --git a/.drone.yml b/.drone.yml index da54c2b..f30258a 100644 --- a/.drone.yml +++ b/.drone.yml @@ -20,6 +20,7 @@ steps: - echo "current git commit is $version" - echo $version > /mnt/botamusique/testing-version - sed -i "s/version = 'git'/version = '$version'/" mumbleBot.py + - ./lang/translate.py - rm -rf .git* - rm -rf web - mkdir /tmp/botamusique diff --git a/command.py b/command.py index d55146c..29d4f48 100644 --- a/command.py +++ b/command.py @@ -6,7 +6,8 @@ import json import re import pymumble_py3 as pymumble -from constants import tr, commands +from constants import tr_cli as tr +from constants import commands import interface import media.system import util diff --git a/configuration.default.ini b/configuration.default.ini index 334c47f..fc5c39e 100644 --- a/configuration.default.ini +++ b/configuration.default.ini @@ -195,159 +195,3 @@ list_webinterface_user = webuserlist add_webinterface_user = webuseradd remove_webinterface_user = webuserdel 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 !{command} {{page}} to navigate. -records_omitted = ... -bad_url = Bad URL requested. -preconfigurated_radio = Preconfigurated Radio available: -unable_download = Unable to download {item}. Removed from the library. -unable_play = Unable to play {item}. Removed from the library. -which_command = Do you mean
{commands} -multiple_matches = File not found! Possible candidates: -queue_contents = Items on the playlist: -queue_empty = Playlist is empty! -invalid_index = Invalid index {index}. Use '!queue' to see your playlist. -now_playing = Playing {item} -radio = Radio -file = File -url_from_playlist = URL -url = URL -radio_item = {title} from {name} added by {user} -file_item = {artist} - {title} added by {user} -url_from_playlist_item = {title} from playlist {playlist} added by {user} -url_item = {title} added by {user} -not_in_my_channel = You're not in my channel, command refused! -pm_not_allowed = Private message aren't allowed. -too_long = {song} is too long, removed from playlist! -download_in_progress = Download of {item} 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 =

Update Available!

Version {new_version} of botamusique is available!
-

Changelog

{changelog}
Send !update to update! -update_successful =

botamusique v{version} Installed!


-

Changelog

{changelog}
Visit our github repo 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 one-shot, repeat, random. -current_mode = Current playback mode is {mode}. -change_mode = Playback mode set to {mode} by {user}. -repeat = Repeat {song} for {n} times. -yt_result = Youtube query result: {result_table} Use !sl {{indexes}} to play the item you want.
- !ytquery -n 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 {tags} to {song}. -added_tags_to_all = Added tags {tags} to songs on the playlist. -removed_tags = Removed tags {tags} from {song}. -removed_tags_from_all = Removed tags {tags} from songs on the playlist. -cleared_tags = Removed all tags from {song}. -cleared_tags_from_all = Removed all tags from songs on the playlist. -shortlist_instruction = Use !sl {indexes} to play the item you want. -auto_paused = Use !play to resume music! -webpage_address= Your own address to access the web interface is {address} -web_user_list = Following users has the privilege to access the web interface:
{users} -user_password_set = Your password has been updated. -command_disabled = {command}: command disabled! - -help =

Commands

- Control - - Playlist - - Music Library - - Other - - -admin_help =

Admin command

- Bot - - Web Interface - - - diff --git a/configuration.example.ini b/configuration.example.ini index 79142e4..13c546c 100644 --- a/configuration.example.ini +++ b/configuration.example.ini @@ -177,18 +177,3 @@ port = 64738 # see configuration.default.ini. Copy options you want to edit into this file. #play_file = file, f #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}!
ちゃんと覚える:大音量で耳が悪くなる! -# -#bad_command = {command}: command not found -#bad_command = {command}: 未知命令,键入'!help'以获取可用命令列表 -#bad_command = {command}がなに?食べれる?おいしいでしか? diff --git a/constants.py b/constants.py index 72cfec0..2db849d 100644 --- a/constants.py +++ b/constants.py @@ -1,24 +1,45 @@ +import json + 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: - string = var.config.get("strings", option) + string = lang_dict['cli'][option] except KeyError: - raise KeyError("Missed strings in configuration file: '{string}'. ".format(string=option) - + "Please restore you configuration file back to default if necessary.") + raise KeyError("Missed strings in language file: '{string}'. ".format(string=option)) + 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: try: formatted = string.format(*argv, **kwargs) return formatted except KeyError as e: raise KeyError( - "Missed/Unexpected placeholder {{{placeholder}}} in string '{string}'. ".format(placeholder=str(e).strip("'"), string=option) - + "Please restore you configuration file back to default if necessary.") + "Missed/Unexpected placeholder {{{placeholder}}} in string " + "'{string}'. ".format(placeholder=str(e).strip("'"), + string=string)) except TypeError: raise KeyError( - "Missed placeholder in string '{string}'. ".format(string=option) - + "Please restore you configuration file back to default if necessary.") + "Missed placeholder in string '{string}'. ".format(string=string)) else: return string @@ -28,5 +49,4 @@ def commands(command): string = var.config.get("commands", command) return string except KeyError: - raise KeyError("Missed command in configuration file: '{string}'. ".format(string=command) - + "Please restore you configuration file back to default if necessary.") + raise KeyError("Missed command in configuration file: '{string}'. ".format(string=command)) diff --git a/lang/translate.py b/lang/translate.py new file mode 100644 index 0000000..bb9dd2f --- /dev/null +++ b/lang/translate.py @@ -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) diff --git a/media/file.py b/media/file.py index 2686bfd..57ccba6 100644 --- a/media/file.py +++ b/media/file.py @@ -9,7 +9,7 @@ from PIL import Image import util import variables as var from media.item import BaseItem, item_builders, item_loaders, item_id_generators, ValidationFailedError -import constants +from constants import tr_cli as tr ''' type : file @@ -75,7 +75,7 @@ class FileItem(BaseItem): if not os.path.exists(self.uri()): self.log.info( "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: self.duration = util.get_media_duration(self.uri()) @@ -185,14 +185,14 @@ class FileItem(BaseItem): ) def format_song_string(self, user): - return constants.tr("file_item", - title=self.title, - artist=self.artist if self.artist else '??', - user=user - ) + return tr("file_item", + title=self.title, + artist=self.artist if self.artist else '??', + user=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: thumbnail_html = '' @@ -208,4 +208,4 @@ class FileItem(BaseItem): return title def display_type(self): - return constants.tr("file") + return tr("file") diff --git a/media/radio.py b/media/radio.py index 1accd31..ae82a73 100644 --- a/media/radio.py +++ b/media/radio.py @@ -7,7 +7,7 @@ import hashlib from media.item import BaseItem from media.item import item_builders, item_loaders, item_id_generators -import constants +from constants import tr_cli as tr log = logging.getLogger("bot") @@ -147,18 +147,18 @@ class RadioItem(BaseItem): ) def format_song_string(self, user): - return constants.tr("radio_item", - url=self.url, - title=get_radio_title(self.url), # the title of current song - name=self.title, # the title of radio station - user=user - ) + return tr("radio_item", + url=self.url, + title=get_radio_title(self.url), # the title of current song + name=self.title, # the title of radio station + user=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): return self.title if self.title else self.url def display_type(self): - return constants.tr("radio") + return tr("radio") diff --git a/media/url.py b/media/url.py index 529d35d..d4d1aa8 100644 --- a/media/url.py +++ b/media/url.py @@ -9,7 +9,7 @@ import glob from io import BytesIO import base64 -import constants +from constants import tr_cli as tr import media import variables as var 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) log.info( "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: self.ready = "validated" self.version += 1 # notify wrapper to save me @@ -138,7 +138,7 @@ class URLItem(BaseItem): if not succeed: self.ready = 'failed' 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): 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) self.ready = "failed" 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): if os.path.isfile(path_thumbnail): @@ -226,14 +226,14 @@ class URLItem(BaseItem): def format_song_string(self, user): if self.ready in ['validated', 'yes']: - return constants.tr("url_item", + return tr("url_item", title=self.title if self.title else "??", url=self.url, user=user) return self.url 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: thumbnail_html = '