Fixed a bug in the translation part of the plugin.
This commit is contained in:
@@ -340,6 +340,10 @@ class IndentationAudio(Plugin):
|
||||
def _on_caret_moved(self, event):
|
||||
"""Handle caret movement events."""
|
||||
try:
|
||||
# Check if plugin is activated first (prevents race conditions during deactivation)
|
||||
if not self._activated or not self._enabled:
|
||||
return
|
||||
|
||||
if not self._beeps_enabled():
|
||||
return
|
||||
|
||||
@@ -674,11 +678,15 @@ class IndentationAudio(Plugin):
|
||||
|
||||
def check_indentation_change(self, obj, line_text):
|
||||
"""Check if indentation has changed and play audio cue if needed.
|
||||
|
||||
|
||||
This method is intended to be called by scripts during line navigation.
|
||||
"""
|
||||
debug.printMessage(debug.LEVEL_INFO, f"IndentationAudio: check_indentation_change called: line='{line_text}'", True)
|
||||
|
||||
# Check if plugin is activated and enabled
|
||||
if not self._activated or not self._enabled:
|
||||
return
|
||||
|
||||
if not line_text or not self._beeps_enabled():
|
||||
return
|
||||
|
||||
|
||||
@@ -20,3 +20,5 @@ will not start the server.
|
||||
if set; otherwise it defaults to 3457.
|
||||
- Toggle interrupt/no-interrupt mode with cthulhu+shift+n.
|
||||
- Toggle translation with cthulhu+control+shift+t (requires translate-shell).
|
||||
- Translation defaults to your system locale; override with `NVDA2CTHULHU_TRANSLATE_TARGET`
|
||||
(e.g., `NVDA2CTHULHU_TRANSLATE_TARGET=en`).
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
"""NVDA to Cthulhu bridge plugin."""
|
||||
|
||||
from .plugin import Nvda2Cthulhu
|
||||
|
||||
__all__ = ['Nvda2Cthulhu']
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
"""NVDA to Cthulhu bridge plugin."""
|
||||
|
||||
import asyncio
|
||||
import locale
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
@@ -46,6 +47,7 @@ except Exception: # pragma: no cover - optional dependency
|
||||
|
||||
from cthulhu.plugin import Plugin, cthulhu_hookimpl
|
||||
from cthulhu import braille
|
||||
from cthulhu import debug
|
||||
from cthulhu import speech
|
||||
from cthulhu import settings_manager
|
||||
|
||||
@@ -90,20 +92,29 @@ class Nvda2Cthulhu(Plugin):
|
||||
"""Bridge server that accepts NVDA to Cthulhu messages."""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.settingsManager = settings_manager.getManager()
|
||||
self.interruptEnabled = True
|
||||
self.serverThread = None
|
||||
self.serverLock = threading.Lock()
|
||||
self.httpServer = None
|
||||
self.ioLoop = None
|
||||
self.asyncioLoop = None
|
||||
self.translationCache = OrderedDict()
|
||||
self.translationCacheLock = threading.Lock()
|
||||
logger.info("NVDA to Cthulhu __init__() called")
|
||||
try:
|
||||
super().__init__()
|
||||
logger.info("NVDA to Cthulhu super().__init__() completed")
|
||||
self.settingsManager = settings_manager.getManager()
|
||||
self.interruptEnabled = True
|
||||
self.serverThread = None
|
||||
self.serverLock = threading.Lock()
|
||||
self.httpServer = None
|
||||
self.ioLoop = None
|
||||
self.asyncioLoop = None
|
||||
self.translationCache = OrderedDict()
|
||||
self.translationCacheLock = threading.Lock()
|
||||
logger.info("NVDA to Cthulhu __init__() completed successfully")
|
||||
except Exception as e:
|
||||
logger.error(f"NVDA to Cthulhu __init__() failed: {e}")
|
||||
raise
|
||||
|
||||
@cthulhu_hookimpl
|
||||
def activate(self, plugin=None):
|
||||
logger.info(f"NVDA to Cthulhu activate() called with plugin={plugin}, self={self}")
|
||||
if plugin is not None and plugin is not self:
|
||||
logger.info(f"NVDA to Cthulhu activate() early return: plugin is not self")
|
||||
return
|
||||
|
||||
logger.info("Activating NVDA to Cthulhu plugin")
|
||||
@@ -300,7 +311,10 @@ class Nvda2Cthulhu(Plugin):
|
||||
if not text or not text.strip():
|
||||
return
|
||||
if self._translation_enabled():
|
||||
text = self._translate_text(text)
|
||||
logger.info(f"NVDA to Cthulhu: translating text: {text[:50]}")
|
||||
translated = self._translate_text(text)
|
||||
logger.info(f"NVDA to Cthulhu: translated to: {translated[:50]}")
|
||||
text = translated
|
||||
speech.speak(text, interrupt=self.interruptEnabled)
|
||||
|
||||
def _handle_braille(self, text):
|
||||
@@ -345,36 +359,81 @@ class Nvda2Cthulhu(Plugin):
|
||||
def _translation_command_available(self):
|
||||
return shutil.which(TRANSLATE_COMMAND[0]) is not None
|
||||
|
||||
def _get_target_language(self):
|
||||
"""Get the target language code from system locale."""
|
||||
try:
|
||||
# Get system locale (e.g., 'en_US.UTF-8')
|
||||
system_locale = locale.getdefaultlocale()[0]
|
||||
if system_locale:
|
||||
# Extract language code (e.g., 'en' from 'en_US')
|
||||
lang_code = system_locale.split('_')[0]
|
||||
return lang_code
|
||||
except Exception as exc:
|
||||
logger.warning(f"NVDA to Cthulhu: failed to get system locale: {exc}")
|
||||
|
||||
# Fallback to environment variable
|
||||
try:
|
||||
lang_env = os.environ.get('LANG', '')
|
||||
if lang_env:
|
||||
# Extract language code from LANG (e.g., 'en' from 'en_US.UTF-8')
|
||||
lang_code = lang_env.split('_')[0]
|
||||
if lang_code:
|
||||
return lang_code
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return None
|
||||
|
||||
def _get_translate_target(self):
|
||||
env_target = os.environ.get("NVDA2CTHULHU_TRANSLATE_TARGET")
|
||||
if env_target:
|
||||
return env_target
|
||||
return self._get_target_language()
|
||||
|
||||
def _translate_text(self, text):
|
||||
cached = self._get_cached_translation(text)
|
||||
if cached is not None:
|
||||
logger.info(f"NVDA to Cthulhu: using cached translation")
|
||||
return cached
|
||||
if not self._translation_command_available():
|
||||
logger.warning("NVDA to Cthulhu translation failed: translate-shell not available")
|
||||
return text
|
||||
|
||||
translate_target = self._get_translate_target()
|
||||
command = list(TRANSLATE_COMMAND)
|
||||
if translate_target:
|
||||
command.append(f":{translate_target}")
|
||||
|
||||
logger.info(f"NVDA to Cthulhu: running trans command on: {text[:50]}")
|
||||
try:
|
||||
result = subprocess.run(
|
||||
TRANSLATE_COMMAND,
|
||||
command,
|
||||
input=text,
|
||||
text=True,
|
||||
encoding="utf-8",
|
||||
errors="replace",
|
||||
capture_output=True,
|
||||
check=False,
|
||||
timeout=TRANSLATE_TIMEOUT
|
||||
)
|
||||
except subprocess.TimeoutExpired:
|
||||
logger.warning("NVDA to Cthulhu translation failed: timed out")
|
||||
debug.printMessage(debug.LEVEL_INFO, "NVDA to Cthulhu translation failed: timed out", True)
|
||||
return text
|
||||
except Exception as exc:
|
||||
logger.warning(f"NVDA to Cthulhu translation failed: {exc}")
|
||||
debug.printMessage(debug.LEVEL_INFO, f"NVDA to Cthulhu translation failed: {exc}", True)
|
||||
return text
|
||||
|
||||
if result.returncode != 0:
|
||||
stderr = result.stderr.strip()
|
||||
if stderr:
|
||||
logger.warning(f"NVDA to Cthulhu translation failed: {stderr}")
|
||||
debug.printMessage(debug.LEVEL_INFO, f"NVDA to Cthulhu translation failed: {stderr}", True)
|
||||
return text
|
||||
|
||||
output = result.stdout.strip()
|
||||
logger.info(f"NVDA to Cthulhu: trans output: {output[:50]}")
|
||||
if not output:
|
||||
return text
|
||||
self._set_cached_translation(text, output)
|
||||
|
||||
@@ -494,7 +494,20 @@ presentChatRoomLast = False
|
||||
presentLiveRegionFromInactiveTab = False
|
||||
|
||||
# Plugins
|
||||
activePlugins = ['DisplayVersion', 'OCR', 'PluginManager', 'HelloCthulhu', 'ByeCthulhu', 'WindowTitleReader']
|
||||
activePlugins = [
|
||||
'PluginManager',
|
||||
'ByeCthulhu',
|
||||
'Clipboard',
|
||||
'HelloCthulhu',
|
||||
'DisplayVersion',
|
||||
'GameMode',
|
||||
'IndentationAudio',
|
||||
'nvda2cthulhu',
|
||||
'OCR',
|
||||
'SpeechHistory',
|
||||
'SSIPProxy',
|
||||
'WindowTitleReader'
|
||||
]
|
||||
pluginSources = []
|
||||
|
||||
# AI Assistant settings (disabled by default for opt-in behavior)
|
||||
|
||||
Reference in New Issue
Block a user