Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
ply==3.11
PyYAML==5.4.1
Unidecode==1.3.2
argostranslate==1.9.6
3 changes: 2 additions & 1 deletion test/samples/de/hello.de.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
drucken("hello world")
etwas = "hello world"
drucken(etwas)
98 changes: 89 additions & 9 deletions universalpython/filters/translate/argos_translator.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,91 @@
def argos_translator(text, source):
if not argos_translate_initialized:
initialize_argos_translate()

if argos_translator is not None:
try:
return argos_translator.translate(text)
except Exception as e:
print(f"Warning: Translation failed - {str(e)}")
import warnings
from functools import lru_cache

# Global state variables
_argos_translate_initialized = False
_argos_translate_available = False
_translation_models = {}

def initialize_argos_translate(source_lang="en", target_lang="en"):
"""Initialize Argos Translate with status messages and package installation"""
global _argos_translate_initialized, _argos_translate_available, _translation_models

if _argos_translate_initialized:
return

_argos_translate_initialized = True

try:
import argostranslate.package
import argostranslate.translate
_argos_translate_available = True

# Update package index
print("Updating Argos Translate package index...")
argostranslate.package.update_package_index()
available_packages = argostranslate.package.get_available_packages()

# Find and install required translation package
package_to_install = next(
(pkg for pkg in available_packages
if pkg.from_code == source_lang and pkg.to_code == target_lang),
None
)

if package_to_install:
print(f"Installing translation package: {source_lang} -> {target_lang}")
argostranslate.package.install_from_path(package_to_install.download())

# Load the installed model
installed_languages = argostranslate.translate.get_installed_languages()
from_lang = next((lang for lang in installed_languages if lang.code == source_lang), None)
to_lang = next((lang for lang in installed_languages if lang.code == target_lang), None)

if from_lang and to_lang:
_translation_models[(source_lang, target_lang)] = from_lang.get_translation(to_lang)
print("Translation package installed successfully.")
else:
warnings.warn("Failed to load installed languages.")
else:
warnings.warn(f"No translation package available for {source_lang} -> {target_lang}")

except ImportError:
warnings.warn("Argos Translate not available. Install with: pip install argostranslate")
_argos_translate_available = False
except Exception as e:
warnings.warn(f"Failed to initialize Argos Translate: {str(e)}")
_argos_translate_available = False

@lru_cache(maxsize=1000)
def argos_translator(text, source_lang="en"):
"""Translate text using Argos Translate with lazy initialization"""
target_lang = "en" # We always translate to English

if not _argos_translate_initialized:
initialize_argos_translate(source_lang, target_lang)

if not _argos_translate_available:
return text

try:
# Check if we have a cached model
if (source_lang, target_lang) not in _translation_models:
# Try to load existing installed model
installed_languages = argostranslate.translate.get_installed_languages()
from_lang = next((lang for lang in installed_languages if lang.code == source_lang), None)
to_lang = next((lang for lang in installed_languages if lang.code == target_lang), None)

if from_lang and to_lang:
_translation_models[(source_lang, target_lang)] = from_lang.get_translation(to_lang)
else:
warnings.warn(f"No translation model available for {source_lang} -> {target_lang}")
return text

# Perform the translation
translation_model = _translation_models[(source_lang, target_lang)]
translated_text = translation_model.translate(text)
return translated_text

except Exception as e:
warnings.warn(f"Translation failed for '{text}': {str(e)}")
return text
17 changes: 9 additions & 8 deletions universalpython/modes/lex.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,14 +271,15 @@ def t_ID(t):
# print ("Value is:", t.value)
# ------------- Debugging ---------------


if args['translate']:
# if args['translate'] == 'argostranslate':
# if t.type == 'ID':
# t.value = unidecode(t.value)
# else:
if t.type == 'ID':
t.value = unidecode(t.value)
if args['translate'] is not False:
if args['translate'] == 'argostranslate':
from universalpython.filters.translate.argos_translator import argos_translator
if t.type == 'ID':
# Only pass the source language, not the whole args dict
t.value = argos_translator(t.value, args.get("source_language", "en"))
else:
if t.type == 'ID':
t.value = unidecode(t.value)
return t

if args["reverse"]:
Expand Down
80 changes: 48 additions & 32 deletions universalpython/universalpython.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,55 +43,62 @@ def build_language_map():
DEFAULT_LANGUAGE_MAP = build_language_map()

def detect_language_from_filename(filename):
"""Detect language from file extension (e.g., my-program.de.py -> german)"""
"""Detect language from file extension (e.g., my-program.de.py -> german)
Returns tuple: (filepath, two_letter_code) or None"""
parts = filename.split('.')

# ------------- Debugging ---------------
# print("filename", filename)
# print("parts", parts)
# print("DEFAULT_LANGUAGE_MAP", DEFAULT_LANGUAGE_MAP)
# ------------- Debugging ---------------


if len(parts) > 2: # Has language code in extension
lang_code = parts[-2].lower()
return DEFAULT_LANGUAGE_MAP.get(lang_code, None)['default']
if lang_code in DEFAULT_LANGUAGE_MAP:
# Return first filepath found and the language code
lang_files = DEFAULT_LANGUAGE_MAP[lang_code]
first_file = next(iter(lang_files.values())) if lang_files else None
return (first_file, lang_code)
return None

def detect_language_from_comment(code):
"""Detect language from comment (e.g., # language:fr)"""
"""Detect language from comment (e.g., # language:fr)
Returns tuple: (filepath, two_letter_code) or None"""
first_lines = code.split('\n')[:5] # Check first 5 lines for comment
for line in first_lines:
match = re.search(r'^#\s*language\s*:\s*(\w+)', line, re.IGNORECASE)
if match:
lang_code = match.group(1).lower()
return DEFAULT_LANGUAGE_MAP.get(lang_code, None)['default']
if lang_code in DEFAULT_LANGUAGE_MAP:
# Return first filepath found and the language code
lang_files = DEFAULT_LANGUAGE_MAP[lang_code]
first_file = next(iter(lang_files.values())) if lang_files else None
return (first_file, lang_code)
return None

def determine_language(args, filename, code):
"""Determine target language based on priority rules"""
# If dictionary is explicitly provided, use that
if args.get('dictionary'):
return args['dictionary']
detected_dictionary = None

# Check for language comment (higher priority)
lang = detect_language_from_comment(code)
if lang:
return lang

# Check file extension
lang = detect_language_from_filename(filename)
if lang:
return lang

# Default to empty (no translation)
return ""
detected_lang = None

# Check detection methods in priority order
if args.get('dictionary'):
detected_dictionary = args['dictionary']
elif args.get('source_language'):
detected_dictionary = DEFAULT_LANGUAGE_MAP.get(args['source_language'], {})['default']
else:
detected_dictionary, detected_lang = detect_language_from_comment(code) or detect_language_from_filename(filename)

# Update source_language with the detected language if not explicitly set
# if not args.get('source_language') and detected_lang:
if detected_lang:
args['source_language'] = detected_lang

return detected_dictionary or ""

def run_module(
mode,
code,
args={
'translate': False,
'dictionary': "",
'source_language': "",
'reverse': False,
'keep': False,
'keep_only': False,
Expand All @@ -111,14 +118,23 @@ def main():
help='File to compile.')

ap.add_argument("-t", "--translate",
action='store_true',
default=False, required=False,
help="Translate variables and functions to English, using the unicode.")
choices=["", "argostranslate", "unicode"],
const="", # Default when --translate is used without value
default=None, # Default when --translate is not used at all
nargs='?', # Makes the argument optional
required=False,
help="Translate variables and functions. Options: "
"no value (unicode), 'argostranslate', or 'unicode'")

ap.add_argument("-d", "--dictionary",
default="", required=False,
help="The dictionary to use to translate the code.")

ap.add_argument("-sl", "--source-language",
default="", required=False,
dest="source_language",
help="The source language of the code (for translation).")

ap.add_argument("-r", "--reverse",
action='store_true',
default=False, required=False,
Expand All @@ -143,10 +159,10 @@ def main():
args = vars(ap.parse_args())

filename = args["file"][0]
code_pyfile = open(filename)
code = code_pyfile.read()
with open(filename) as code_pyfile:
code = code_pyfile.read()

# Determine language if not explicitly provided
# Determine language and update source_language
args['dictionary'] = determine_language(args, filename, code)

# Default mode is 'lex' if not specified
Expand Down