local lang = jarvis.context.language local phrase = (jarvis.context.phrase or ""):lower() local target_languages = { ["английский"] = "English", ["english"] = "English", ["русский"] = "Russian", ["russian"] = "Russian", ["украинский"] = "Ukrainian", ["ukrainian"] = "Ukrainian", ["українську"] = "Ukrainian", ["немецкий"] = "German", ["german"] = "German", ["німецьку"] = "German", ["французский"]= "French", ["french"] = "French", ["испанский"] = "Spanish", ["spanish"] = "Spanish", ["итальянский"]= "Italian", ["китайский"] = "Chinese", ["chinese"] = "Chinese", ["японский"] = "Japanese", ["japanese"] = "Japanese", ["польский"] = "Polish", ["турецкий"] = "Turkish", } local triggers_with_lang = { "переведи на ", "translate to ", "переклади на ", "how to say in ", } local generic_triggers = { "переведи", "translate", "переклади", "как по ", "how to say", } local target = nil local body = phrase for _, t in ipairs(triggers_with_lang) do local s, e = string.find(body, t, 1, true) if s == 1 then local rest = body:sub(e + 1) local first_word = rest:match("^(%S+)") if first_word and target_languages[first_word] then target = target_languages[first_word] body = rest:sub(#first_word + 1) break end end end if not target then for _, t in ipairs(generic_triggers) do local s, e = string.find(body, t, 1, true) if s == 1 then body = body:sub(e + 1) break end end end body = body:gsub("^%s+", ""):gsub("%s+$", "") if body == "" then jarvis.system.notify( "Translate", lang == "ru" and "Что переводить?" or "What to translate?" ) jarvis.audio.play_error() return { chain = false } end local token = jarvis.system.env("GROQ_TOKEN") if not token or token == "" then jarvis.system.notify("Translate", "GROQ_TOKEN не задан") jarvis.audio.play_error() return { chain = false } end local base = jarvis.system.env("GROQ_BASE_URL"); if not base or base == "" then base = "https://api.groq.com/openai/v1" end local model = jarvis.system.env("GROQ_MODEL"); if not model or model == "" then model = "llama-3.3-70b-versatile" end local target_label = target or (lang == "ru" and "English" or "Russian") local sys = string.format( "You are a professional translator. Translate the user message into %s. " .. "Output ONLY the translation, no explanations, no quotes, no prefix. " .. "Preserve idioms and tone.", target_label ) jarvis.log("info", "translate -> " .. target_label .. " : " .. body) local payload = { model = model, messages = { { role = "system", content = sys }, { role = "user", content = body }, }, max_tokens = 512, temperature = 0.3, } local res = jarvis.http.post_json(base .. "/chat/completions", payload, { Authorization = "Bearer " .. token }) if not res.ok then jarvis.log("error", "translate api failed: " .. tostring(res.status) .. " " .. tostring(res.body):sub(1, 200)) jarvis.system.notify("Translate", "Ошибка API") jarvis.audio.play_error() return { chain = false } end local body_resp = res.body or "" local content = body_resp:match('"content"%s*:%s*"(.-[^\\])"') if not content then jarvis.system.notify("Translate", "Не смог распарсить ответ") jarvis.audio.play_error() return { chain = false } end content = content:gsub('\\n', '\n'):gsub('\\"', '"'):gsub('\\\\', '\\'):gsub('\\t', '\t') content = content:gsub("^%s+", ""):gsub("%s+$", "") jarvis.system.clipboard.set(content) jarvis.system.notify(target_label, content:sub(1, 200)) jarvis.log("info", "translation: " .. content) local sapi_text = content:gsub("'", "''"):gsub("\r?\n", " ") local ps = string.format( [[Add-Type -AssemblyName System.Speech; $s = New-Object System.Speech.Synthesis.SpeechSynthesizer; $iso='%s'; foreach ($v in $s.GetInstalledVoices()) { if ($v.VoiceInfo.Culture.TwoLetterISOLanguageName -eq $iso) { try { $s.SelectVoice($v.VoiceInfo.Name); break } catch {} } } $s.Speak('%s')]], (target_label == "Russian" and "ru") or (target_label == "Ukrainian" and "uk") or "en", sapi_text ) jarvis.system.exec(string.format('powershell -NoProfile -ExecutionPolicy Bypass -Command "%s"', ps:gsub('"', '\\"'))) jarvis.audio.play_ok() return { chain = false }