From 7bb0ec1e2e06de0f5669f3a009f9e70d0b0290b2 Mon Sep 17 00:00:00 2001
From: ! Freaku <92618708+Freaku17@users.noreply.github.com>
Date: Sat, 29 Aug 2026 16:34:12 +0530
Subject: [PATCH 1/2] CRITICAL FIX: `translate.py`
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
• If YOU were using `translate.py` before v1.7.40 & later updated. It worked fine.
• However, if YOU installed it for the first time after v1.7.40
• Your config would not be saved.
• Meaning you were unable to change your language preferences.
• Reason is because, Eric (developer of bombsquad). Changed the API; instead of `ba.app.lang` made new `ba.app.locale`
• This changed config "keys" into another type.
• This commit/update has **FIXED** this issued entirely.
• This also fixes those whose config may have been broken. (Only if it was caused by translate.py)
Other changes:
• Use `ba.CallPartial` instead of `ba.CallStrict`
• Import `urllib` submodules
• Use `daemon` for threads
• If using Translate without internet, prints a warning to connect to internet.
• Commit config instantly instead of waiting for bombsquad to save it (which it does automatically periodically)
• Exact text_widget translate!!!
(Wanted to implement this since start, until now it used `for` loop to find text & replace with translated message.
Issue was, if someone spammed same message & you clicked translate on 1 message.
It would've translated the first message.
Instead of the "exact" message you clicked to translate.
This basically fixes it, by directly passing the entire text_widget. Yippie! )
---
plugins/utilities/translate.py | 261 +++++++++++++++++++++++++--------
1 file changed, 198 insertions(+), 63 deletions(-)
diff --git a/plugins/utilities/translate.py b/plugins/utilities/translate.py
index 241f7942..6f571140 100644
--- a/plugins/utilities/translate.py
+++ b/plugins/utilities/translate.py
@@ -9,59 +9,159 @@
import bauiv1 as bui
from bauiv1lib.popup import PopupMenu
import bauiv1lib.party
-import urllib
+import urllib.parse
+import urllib.request
import threading
import random
show_translate_result = True
config = babase.app.config
-default_config = {'O Source Trans Lang': 'Auto Detect', 'O Target Trans Lang': babase.app.locale.default_locale,
- 'Y Source Trans Lang': 'Auto Detect', 'Y Target Trans Lang': babase.app.locale.default_locale}
-
-for key in default_config:
- if not key in config:
- config[key] = default_config[key]
-
-translate_languages = {'Auto Detect': 'auto', 'Arabic': 'ar', 'Chinese (simplified)': 'zh-CN', 'Chinese (traditional)': 'zh-TW', 'Croatian': 'hr', 'Czech': 'cs',
- 'Danish': 'da', 'Dutch': 'nl', 'English': 'en', 'Esperanto': 'eo',
- 'Finnish': 'fi',
- 'Tagalog': 'tl', 'French': 'fr', 'German': 'de', 'Greek': 'el',
- 'Hindi': 'hi', 'Hungarian': 'hu', 'Indonesian': 'id', 'Italian': 'it',
- 'Japanese': 'ja',
- 'Korean': 'ko', 'Malay': 'ms', 'Malayalam': 'ml', 'Marathi': 'mr', 'Persian': 'fa', 'Polish': 'pl',
- 'Portuguese': 'pt', 'Romanian': 'ro', 'Russian': 'ru', 'Serbian': 'sr',
- 'Slovak': 'sk', 'Spanish': 'es', 'Swedish': 'sv', 'Tamil': 'ta',
- 'Telugu': 'te',
- 'Thai': 'th', 'Turkish': 'tr', 'Ukrainian': 'uk', 'Vietnamese': 'vi'}
-available_translate_languages = []
-for lang in translate_languages:
- available_translate_languages.append(lang)
+
+# Convert Ballistica Locale -> this mod's language name.
+def get_default_translate_language():
+ locale = babase.app.locale.default_locale
+
+ # Ballistica 1.7.40 Locale objects have a .value such as 'eng'.
+ locale_value = getattr(locale, 'value', str(locale))
+
+ locale_map = {
+ 'eng': 'English',
+ 'ara': 'Arabic',
+ 'zho': 'Chinese (simplified)',
+ 'hrv': 'Croatian',
+ 'ces': 'Czech',
+ 'dan': 'Danish',
+ 'nld': 'Dutch',
+ 'fin': 'Finnish',
+ 'fra': 'French',
+ 'deu': 'German',
+ 'ell': 'Greek',
+ 'hin': 'Hindi',
+ 'hun': 'Hungarian',
+ 'ind': 'Indonesian',
+ 'ita': 'Italian',
+ 'jpn': 'Japanese',
+ 'kor': 'Korean',
+ 'msa': 'Malay',
+ 'mal': 'Malayalam',
+ 'mar': 'Marathi',
+ 'fas': 'Persian',
+ 'pol': 'Polish',
+ 'por': 'Portuguese',
+ 'ron': 'Romanian',
+ 'rus': 'Russian',
+ 'srp': 'Serbian',
+ 'slk': 'Slovak',
+ 'spa': 'Spanish',
+ 'swe': 'Swedish',
+ 'tam': 'Tamil',
+ 'tel': 'Telugu',
+ 'tha': 'Thai',
+ 'tur': 'Turkish',
+ 'ukr': 'Ukrainian',
+ 'vie': 'Vietnamese',
+ 'tgl': 'Tagalog',
+ }
+
+ return locale_map.get(locale_value, 'English')
+
+
+default_language = get_default_translate_language()
+
+default_config = {
+ 'O Source Trans Lang': 'Auto Detect',
+ 'O Target Trans Lang': default_language,
+ 'Y Source Trans Lang': 'Auto Detect',
+ 'Y Target Trans Lang': default_language,
+}
+
+for key, value in default_config.items():
+ if key not in config:
+ config[key] = value
+
+# Repair configs created with the old Locale-object behavior.
+for key in ('O Target Trans Lang', 'Y Target Trans Lang'):
+ if not isinstance(config[key], str):
+ config[key] = default_language
+
+
+
+# Language names -> Google Translate language codes.
+translate_languages = {
+ 'Auto Detect': 'auto',
+ 'Arabic': 'ar',
+ 'Chinese (simplified)': 'zh-CN',
+ 'Chinese (traditional)': 'zh-TW',
+ 'Croatian': 'hr',
+ 'Czech': 'cs',
+ 'Danish': 'da',
+ 'Dutch': 'nl',
+ 'English': 'en',
+ 'Esperanto': 'eo',
+ 'Finnish': 'fi',
+ 'Tagalog': 'tl',
+ 'French': 'fr',
+ 'German': 'de',
+ 'Greek': 'el',
+ 'Hindi': 'hi',
+ 'Hungarian': 'hu',
+ 'Indonesian': 'id',
+ 'Italian': 'it',
+ 'Japanese': 'ja',
+ 'Korean': 'ko',
+ 'Malay': 'ms',
+ 'Malayalam': 'ml',
+ 'Marathi': 'mr',
+ 'Persian': 'fa',
+ 'Polish': 'pl',
+ 'Portuguese': 'pt',
+ 'Romanian': 'ro',
+ 'Russian': 'ru',
+ 'Serbian': 'sr',
+ 'Slovak': 'sk',
+ 'Spanish': 'es',
+ 'Swedish': 'sv',
+ 'Tamil': 'ta',
+ 'Telugu': 'te',
+ 'Thai': 'th',
+ 'Turkish': 'tr',
+ 'Ukrainian': 'uk',
+ 'Vietnamese': 'vi',
+}
+
+available_translate_languages = list(translate_languages.keys())
available_translate_languages.sort()
+
available_translate_languages.remove('Auto Detect')
available_translate_languages.insert(0, 'Auto Detect')
+
def translate(text, _callback, source='auto', target='en'):
- text = urllib.parse.quote(text)
- url = f'https://translate.google.com/m?tl={target}&sl={source}&q={text}'
- request = urllib.request.Request(url)
- data = urllib.request.urlopen(request).read().decode('utf-8')
- result = data[(data.find('"result-container">'))+len('"result-container">')
- :data.find('
')]
- replace_list = [(''', '\''), ('"', '"'), ('&', '&')]
- for i in replace_list:
- result = result.replace(i[0], i[1])
- if show_translate_result:
- bui.pushcall(bui.CallPartial(_callback, result), from_other_thread=True)
+ try:
+ text = urllib.parse.quote(text)
+ url = f'https://translate.google.com/m?tl={target}&sl={source}&q={text}'
+ request = urllib.request.Request(url)
+ data = urllib.request.urlopen(request).read().decode('utf-8')
+ result = data[(data.find('"result-container">'))+len('"result-container">')
+ :data.find('
')]
+ replace_list = [(''', '\''), ('"', '"'), ('&', '&')]
+ for i in replace_list:
+ result = result.replace(i[0], i[1])
+ if show_translate_result:
+ bui.pushcall(bui.CallPartial(_callback, result), from_other_thread=True)
+ except Exception:
+ print('Translation failed. Please connect to the internet.')
class NewPW(bauiv1lib.party.PartyWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
- self._last_msg_clicked: str = None
- self._last_time_pressed_msg: float = 0.0
- self._last_time_pressed_translate: float = 0.0
- self._double_press_interval: float = 0.3
+ self._last_msg_clicked = None
+ self._last_time_pressed_msg = 0.0
+ self._last_time_pressed_translate = 0.0
+ self._double_press_interval = 0.3
+ self._last_translate_widget = None
bui.buttonwidget(
parent=self._root_widget,
size=(50, 35),
@@ -74,22 +174,41 @@ def __init__(self, *args, **kwargs):
def _translate_your_chat(self):
global show_translate_result
- if (babase.apptime() - self._last_time_pressed_translate < self._double_press_interval):
+
+ if (
+ babase.apptime() - self._last_time_pressed_translate
+ < self._double_press_interval
+ and self._last_translate_widget is self._text_field
+ ):
show_translate_result = False
self._last_time_pressed_translate = 0.0
+ self._last_translate_widget = None
TranslateWindow()
return
show_translate_result = True
self._last_time_pressed_translate = babase.apptime()
+ self._last_translate_widget = self._text_field
+
+ text = str(bui.textwidget(query=self._text_field))
def _apply_translation(translated):
if self._text_field.exists():
- bui.textwidget(edit=self._text_field, text=translated)
- translated = threading.Thread(target=translate, args=(str(bui.textwidget(query=self._text_field)),
- _apply_translation,
- translate_languages[config['Y Source Trans Lang']],
- translate_languages[config['Y Target Trans Lang']])).start()
+ bui.textwidget(
+ edit=self._text_field,
+ text=translated
+ )
+
+ threading.Thread(
+ target=translate,
+ args=(
+ text,
+ _apply_translation,
+ translate_languages[config['Y Source Trans Lang']],
+ translate_languages[config['Y Target Trans Lang']]
+ ),
+ daemon=True
+ ).start()
def _add_msg(self, msg: str):
txt = bui.textwidget(
@@ -113,35 +232,50 @@ def _add_msg(self, msg: str):
bui.containerwidget(edit=self._columnwidget, visible_child=txt)
bui.textwidget(edit=txt,
- on_activate_call=bui.CallPartial(self._translate_other, msg),
+ on_activate_call=bui.CallPartial(self._translate_other, txt, msg),
click_activate=True)
- def _translate_other(self, msg: str):
+ def _translate_other(self, txt, msg):
global show_translate_result
- if (babase.apptime() - self._last_time_pressed_msg < self._double_press_interval) and (self._last_msg_clicked == msg):
+
+ if (
+ babase.apptime() - self._last_time_pressed_msg
+ < self._double_press_interval
+ and self._last_msg_clicked == txt
+ ):
show_translate_result = False
self._last_time_pressed_msg = 0.0
self._copy_msg(msg)
return
show_translate_result = True
- self._last_msg_clicked = msg
+ self._last_msg_clicked = txt
self._last_time_pressed_msg = babase.apptime()
+ nickname = ''
+ split_msg = msg
+
if len(msg.split(':')) > 1:
- self._nickname = msg.split(':')[0]+': '
+ nickname = msg.split(':')[0] + ': '
split_msg = ':'.join(msg.split(':')[1:])[1:]
-
+
def _apply_translation(translated):
- for i in self._chat_texts:
- if bui.textwidget(query=i) == msg:
- bui.textwidget(edit=i, text=self._nickname + translated)
- break
-
- self._translated_msg = threading.Thread(target=translate, args=(split_msg,
- _apply_translation,
- translate_languages[config['O Source Trans Lang']],
- translate_languages[config['O Target Trans Lang']])).start()
+ if txt.exists():
+ bui.textwidget(
+ edit=txt,
+ text=nickname + translated
+ )
+
+ threading.Thread(
+ target=translate,
+ args=(
+ split_msg,
+ _apply_translation,
+ translate_languages[config['O Source Trans Lang']],
+ translate_languages[config['O Target Trans Lang']]
+ ),
+ daemon=True
+ ).start()
class TranslateWindow:
@@ -200,7 +334,7 @@ def __init__(self):
self.other_source_button = PopupMenu(parent=self._root_widget,
position=(54, 140),
autoselect=False,
- on_value_change_call=babase.CallStrict(
+ on_value_change_call=babase.CallPartial(
self._set_translate_language, 'O Source Trans Lang'),
choices=available_translate_languages,
button_size=(150, 30),
@@ -209,7 +343,7 @@ def __init__(self):
self.other_target_button = PopupMenu(parent=self._root_widget,
position=(243, 140),
autoselect=False,
- on_value_change_call=babase.CallStrict(
+ on_value_change_call=babase.CallPartial(
self._set_translate_language, 'O Target Trans Lang'),
choices=available_translate_languages[1:],
button_size=(150, 30),
@@ -218,7 +352,7 @@ def __init__(self):
self.your_source_button = PopupMenu(parent=self._root_widget,
position=(54, 55),
autoselect=False,
- on_value_change_call=babase.CallStrict(
+ on_value_change_call=babase.CallPartial(
self._set_translate_language, 'Y Source Trans Lang'),
choices=available_translate_languages,
button_size=(150, 30),
@@ -227,7 +361,7 @@ def __init__(self):
self.your_target_button = PopupMenu(parent=self._root_widget,
position=(243, 55),
autoselect=False,
- on_value_change_call=babase.CallStrict(
+ on_value_change_call=babase.CallPartial(
self._set_translate_language, 'Y Target Trans Lang'),
choices=available_translate_languages[1:],
button_size=(150, 30),
@@ -235,6 +369,7 @@ def __init__(self):
def _set_translate_language(self, lang, choice):
config[lang] = choice
+ config.commit()
def _back(self, sound=False):
self.other_source_button = None
@@ -256,4 +391,4 @@ def has_settings_ui(self):
return True
def show_settings_ui(self, source_widget):
- TranslateWindow()
+ TranslateWindow()
\ No newline at end of file
From dda00e094a50ce208f388e93163a90da2394518c Mon Sep 17 00:00:00 2001
From: ! Freaku <92618708+Freaku17@users.noreply.github.com>
Date: Sat, 29 Aug 2026 16:35:18 +0530
Subject: [PATCH 2/2] Update utilities.json
---
plugins/utilities.json | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/plugins/utilities.json b/plugins/utilities.json
index d9e3cd55..df77fec0 100644
--- a/plugins/utilities.json
+++ b/plugins/utilities.json
@@ -388,6 +388,7 @@
}
],
"versions": {
+ "1.1.4": null,
"1.1.3": {
"api_version": 9,
"commit_sha": "23f90dc",
@@ -2585,4 +2586,4 @@
}
}
}
-}
\ No newline at end of file
+}