refactor: extract UI translations, themes, and language detection into separate modules
- Create ui-translations.js with UI_TRANSLATIONS object and translation functions - Create ui-themes.js with theme detection and styling functions - Create language-detection.js with AI-powered language detection - Remove duplicate code from ui.js, reducing from 5600+ to 4741 lines - Fix import assignments and syntax errors - All functionality preserved while improving code organization
This commit is contained in:
parent
c263cb9d56
commit
d3c5e31bde
4 changed files with 466 additions and 890 deletions
97
src/language-detection.js
Normal file
97
src/language-detection.js
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
// Language detection functionality using AI and fallback methods
|
||||
|
||||
import { API_KEY } from "./config.js";
|
||||
|
||||
// Fast AI-powered language detection using Hermes
|
||||
export async function detectPageLanguage() {
|
||||
try {
|
||||
// Get page content as markdown
|
||||
const pageHtml = document.documentElement.outerHTML;
|
||||
const pageMarkdown = htmlToMarkdown(pageHtml);
|
||||
|
||||
// Take a sample (first 1000 characters) for speed
|
||||
const sample = pageMarkdown.substring(0, 1000);
|
||||
|
||||
// Import required modules
|
||||
const { getSelectedModel, getSelectedModelEndpoint } = await import(
|
||||
"./models.js"
|
||||
);
|
||||
const apiUrl = `${getSelectedModelEndpoint()}/chat/completions`;
|
||||
|
||||
const response = await fetch(apiUrl, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${API_KEY}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: getSelectedModel(),
|
||||
messages: [
|
||||
{
|
||||
role: "system",
|
||||
content:
|
||||
"You are a language classifier. Respond with ONLY the 2-letter ISO language code (en, es, fr, de, etc.) for the detected language. No explanations, just the code.",
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
content: `Detect the language of this text:\n\n${sample}`,
|
||||
},
|
||||
],
|
||||
temperature: 0.1,
|
||||
max_tokens: 10,
|
||||
stream: false,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
console.warn(
|
||||
"Language detection failed, falling back to browser language",
|
||||
);
|
||||
return fallbackLanguageDetection();
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
const detectedLang = data.choices[0].message.content.trim().toLowerCase();
|
||||
|
||||
// Validate it's a reasonable language code (2-3 chars)
|
||||
if (/^[a-z]{2,3}$/.test(detectedLang)) {
|
||||
console.log("AI detected language:", detectedLang);
|
||||
return detectedLang;
|
||||
}
|
||||
console.warn("Invalid language code from AI:", detectedLang);
|
||||
return fallbackLanguageDetection();
|
||||
} catch (error) {
|
||||
console.warn("Language detection error:", error);
|
||||
return fallbackLanguageDetection();
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback language detection using DOM attributes
|
||||
export function fallbackLanguageDetection() {
|
||||
// Check html lang attribute first
|
||||
const htmlLang = document.documentElement.lang;
|
||||
if (htmlLang) {
|
||||
const langCode = htmlLang.split("-")[0].toLowerCase();
|
||||
return langCode;
|
||||
}
|
||||
|
||||
// Fallback to browser language
|
||||
const browserLang = navigator.language || navigator.userLanguage;
|
||||
if (browserLang) {
|
||||
const langCode = browserLang.split("-")[0].toLowerCase();
|
||||
return langCode;
|
||||
}
|
||||
|
||||
// Final fallback
|
||||
return "en";
|
||||
}
|
||||
|
||||
// Simple HTML to markdown conversion (for language detection)
|
||||
function htmlToMarkdown(html) {
|
||||
// Create a temporary element to parse HTML
|
||||
const tempDiv = document.createElement("div");
|
||||
tempDiv.innerHTML = html;
|
||||
|
||||
// Extract text content
|
||||
return tempDiv.textContent || tempDiv.innerText || "";
|
||||
}
|
||||
92
src/ui-themes.js
Normal file
92
src/ui-themes.js
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
// Theme detection and styling functionality
|
||||
|
||||
// Theme detection and styling functions
|
||||
export function detectCurrentTheme() {
|
||||
const isDark =
|
||||
document.documentElement.getAttribute("data-theme") === "dark" ||
|
||||
(window.matchMedia?.("(prefers-color-scheme: dark)").matches &&
|
||||
!document.documentElement.getAttribute("data-theme"));
|
||||
|
||||
return isDark ? "dark" : "light";
|
||||
}
|
||||
|
||||
export function getThemeColors(theme = detectCurrentTheme()) {
|
||||
if (theme === "dark") {
|
||||
return {
|
||||
// Main backgrounds
|
||||
modalBackground: "#1a1a1a",
|
||||
contentBackground: "#2d2d2d",
|
||||
panelBackground: "#262626",
|
||||
|
||||
// Text colors
|
||||
primaryText: "#ffffff",
|
||||
secondaryText: "#cccccc",
|
||||
mutedText: "#999999",
|
||||
|
||||
// Borders and dividers
|
||||
borderColor: "#444444",
|
||||
dividerColor: "#333333",
|
||||
focusBorder: "#0066cc",
|
||||
|
||||
// Buttons
|
||||
buttonBackground: "#404040",
|
||||
buttonHover: "#4a4a4a",
|
||||
buttonBorder: "#555555",
|
||||
|
||||
// Shadows
|
||||
lightShadow: "rgba(0, 0, 0, 0.3)",
|
||||
heavyShadow: "rgba(0, 0, 0, 0.5)",
|
||||
|
||||
// Action button styling
|
||||
actionBackground: "rgba(255, 255, 255, 0.1)",
|
||||
actionHover: "rgba(255, 255, 255, 0.2)",
|
||||
actionBorder: "rgba(255, 255, 255, 0.2)",
|
||||
};
|
||||
}
|
||||
return {
|
||||
// Main backgrounds
|
||||
modalBackground: "#ffffff",
|
||||
contentBackground: "#f8f9fa",
|
||||
panelBackground: "#f1f3f5",
|
||||
|
||||
// Text colors
|
||||
primaryText: "#212529",
|
||||
secondaryText: "#495057",
|
||||
mutedText: "#6c757d",
|
||||
|
||||
// Borders and dividers
|
||||
borderColor: "#dee2e6",
|
||||
dividerColor: "#e9ecef",
|
||||
focusBorder: "#80bdff",
|
||||
|
||||
// Buttons
|
||||
buttonBackground: "#ffffff",
|
||||
buttonHover: "#f8f9fa",
|
||||
buttonBorder: "#dee2e6",
|
||||
|
||||
// Shadows
|
||||
lightShadow: "rgba(0, 0, 0, 0.1)",
|
||||
heavyShadow: "rgba(0, 0, 0, 0.2)",
|
||||
|
||||
// Action button styling
|
||||
actionBackground: "rgba(255, 255, 255, 0.2)",
|
||||
actionHover: "rgba(255, 255, 255, 0.3)",
|
||||
actionBorder: "rgba(255, 255, 255, 0.3)",
|
||||
};
|
||||
}
|
||||
|
||||
// Initialize ChunkFive font with absolute path
|
||||
export function initializeChunkFiveFont() {
|
||||
const fontFaceStyle = document.createElement("style");
|
||||
fontFaceStyle.textContent = `
|
||||
@font-face {
|
||||
font-family: "ChunkFiveRegular";
|
||||
src: url("https://uncloseai.com/css/chunkfive/chunkfive-regular-webfont.woff2") format("woff2"),
|
||||
url("https://uncloseai.com/css/chunkfive/chunkfive-regular-webfont.woff") format("woff");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(fontFaceStyle);
|
||||
}
|
||||
259
src/ui-translations.js
Normal file
259
src/ui-translations.js
Normal file
|
|
@ -0,0 +1,259 @@
|
|||
// UI Translations for all supported languages
|
||||
// This file contains all user interface text translations
|
||||
|
||||
// Complete UI Translations for all supported languages
|
||||
export const UI_TRANSLATIONS = {
|
||||
en: {
|
||||
// Main buttons
|
||||
readPage: "📖 Read Page",
|
||||
ttsAnything: "🔊 TTS Anything",
|
||||
translate: "🌐 Translate",
|
||||
smartTranslate: "🤖 Smart Translate",
|
||||
uploadFile: "📁 Upload File",
|
||||
|
||||
// Settings
|
||||
modelLabel: "AI Model:",
|
||||
voiceLabel: "TTS Voice:",
|
||||
languageLabel: "Interface Language:",
|
||||
quickActions: "Quick Actions:",
|
||||
|
||||
// Actions
|
||||
refreshModels: "🔄 Refresh Models",
|
||||
clearChat: "🗑️ Clear Chat",
|
||||
fullChatRaw: "📋 Full Chat Raw",
|
||||
fullChatHTML: "📄 Full Chat HTML",
|
||||
fullPageRaw: "📝 Full Page Raw",
|
||||
downloadChat: "💾 Download Chat",
|
||||
|
||||
// Chat UI
|
||||
sendButton: "Send",
|
||||
typePlaceholder: "Type your message...",
|
||||
clearChatConfirm: "Clear all conversation history?",
|
||||
|
||||
// Modal headers
|
||||
aiAssistantTitle: "AI Assistant",
|
||||
ttsModalTitle: "Text-to-Speech",
|
||||
translateModalTitle: "Translate Page",
|
||||
|
||||
// System messages
|
||||
pageContentPrefix: "Here's the content of the webpage:",
|
||||
additionalContext:
|
||||
"You are embedded on this page. When answering questions, consider the page content and context.",
|
||||
|
||||
// Notifications and alerts
|
||||
languageChanged:
|
||||
"Language set to {lang}. Refresh the page to see all interface elements in your preferred language.",
|
||||
translationFailed: "Translation failed: {error}",
|
||||
ttsFailed: "TTS failed: {error}",
|
||||
fullChatCopied: "Full chat copied as raw markdown!",
|
||||
fullChatHTMLCopied: "Full chat copied as HTML!",
|
||||
fullPageCopied: "Full page copied as markdown!",
|
||||
failedToCopy: "Failed to copy: {error}",
|
||||
failedToCopyPage: "Failed to copy page: {error}",
|
||||
failedToCopyCode: "Failed to copy code: {error}",
|
||||
errorOpeningModal: "Error opening AI modal: {error}",
|
||||
failedToReadPage: "Failed to read page: {error}",
|
||||
languageDetectionFailed: "Language detection failed: {error}",
|
||||
|
||||
// Dropdown labels
|
||||
currentPage: "Current page: {lang}",
|
||||
translatingTo: "Translating...",
|
||||
detectingLanguage: "🔍 Detecting...",
|
||||
|
||||
// Intro/header text
|
||||
hermesIntro:
|
||||
'presents nous research\'s <a href="https://nousresearch.com/hermes3/" target="_blank" style="color: rgba(255,255,255,0.9); text-decoration: underline;">hermes</a> large language model',
|
||||
discussingPage: "You are discussing",
|
||||
|
||||
// TTS placeholder
|
||||
ttsPlaceholder:
|
||||
"text-to-speech: write any message & have the artificial intelligence speak it!",
|
||||
translatePlaceholder: "Enter any text to translate...",
|
||||
|
||||
// More alerts and messages
|
||||
pleaseEnterText: "Please enter some text first!",
|
||||
pleaseSelectFile: "Please select a file first!",
|
||||
pleaseEnterTranslateText: "Please enter some text to translate!",
|
||||
aiModalNotAvailable: "AI modal not available in this context",
|
||||
},
|
||||
|
||||
// Chinese (Simplified)
|
||||
zh: {
|
||||
readPage: "📖 阅读页面",
|
||||
ttsAnything: "🔊 语音合成",
|
||||
translate: "🌐 翻译",
|
||||
smartTranslate: "🤖 智能翻译",
|
||||
uploadFile: "📁 上传文件",
|
||||
modelLabel: "AI 模型:",
|
||||
voiceLabel: "语音类型:",
|
||||
languageLabel: "界面语言:",
|
||||
quickActions: "快捷操作:",
|
||||
refreshModels: "🔄 刷新模型",
|
||||
clearChat: "🗑️ 清除聊天",
|
||||
fullChatRaw: "📋 完整聊天记录",
|
||||
fullChatHTML: "📄 完整聊天HTML",
|
||||
fullPageRaw: "📝 完整页面原始",
|
||||
downloadChat: "💾 下载聊天",
|
||||
sendButton: "发送",
|
||||
typePlaceholder: "输入您的消息...",
|
||||
clearChatConfirm: "清除所有对话历史记录?",
|
||||
aiAssistantTitle: "AI助手",
|
||||
ttsModalTitle: "文本转语音",
|
||||
translateModalTitle: "翻译页面",
|
||||
pageContentPrefix: "以下是网页内容:",
|
||||
additionalContext: "您已嵌入此页面。回答问题时,请考虑页面内容和上下文。",
|
||||
languageChanged:
|
||||
"语言设置为 {lang}。刷新页面以查看所有界面元素的首选语言。",
|
||||
translationFailed: "翻译失败:{error}",
|
||||
ttsFailed: "语音合成失败:{error}",
|
||||
fullChatCopied: "完整聊天记录已复制为markdown!",
|
||||
fullChatHTMLCopied: "完整聊天记录已复制为HTML!",
|
||||
fullPageCopied: "完整页面已复制为markdown!",
|
||||
failedToCopy: "复制失败:{error}",
|
||||
failedToCopyPage: "复制页面失败:{error}",
|
||||
failedToCopyCode: "复制代码失败:{error}",
|
||||
errorOpeningModal: "打开AI模态框时出错:{error}",
|
||||
failedToReadPage: "读取页面失败:{error}",
|
||||
languageDetectionFailed: "语言检测失败:{error}",
|
||||
currentPage: "当前页面:{lang}",
|
||||
translatingTo: "翻译中...",
|
||||
detectingLanguage: "🔍 检测中...",
|
||||
hermesIntro:
|
||||
'呈现nous research的<a href="https://nousresearch.com/hermes3/" target="_blank" style="color: rgba(255,255,255,0.9); text-decoration: underline;">hermes</a>大型语言模型',
|
||||
discussingPage: "您正在讨论",
|
||||
ttsPlaceholder: "文本转语音:写任何消息,让人工智能朗读出来!",
|
||||
translatePlaceholder: "输入任何要翻译的文本...",
|
||||
pleaseEnterText: "请先输入一些文本!",
|
||||
pleaseSelectFile: "请先选择一个文件!",
|
||||
pleaseEnterTranslateText: "请输入一些要翻译的文本!",
|
||||
aiModalNotAvailable: "AI模态框在此上下文中不可用",
|
||||
},
|
||||
|
||||
// Hindi
|
||||
hi: {
|
||||
readPage: "📖 पृष्ठ पढ़ें",
|
||||
ttsAnything: "🔊 TTS कुछ भी",
|
||||
translate: "🌐 अनुवाद करें",
|
||||
smartTranslate: "🤖 स्मार्ट अनुवाद",
|
||||
uploadFile: "📁 फ़ाइल अपलोड करें",
|
||||
modelLabel: "AI मॉडल:",
|
||||
voiceLabel: "TTS आवाज़:",
|
||||
languageLabel: "इंटरफ़ेस भाषा:",
|
||||
quickActions: "त्वरित क्रियाएं:",
|
||||
refreshModels: "🔄 मॉडल रीफ्रेश करें",
|
||||
clearChat: "🗑️ चैट साफ़ करें",
|
||||
fullChatRaw: "📋 पूर्ण चैट रॉ",
|
||||
fullChatHTML: "📄 पूर्ण चैट HTML",
|
||||
fullPageRaw: "📝 पूर्ण पृष्ठ रॉ",
|
||||
downloadChat: "💾 चैट डाउनलोड करें",
|
||||
sendButton: "भेजें",
|
||||
typePlaceholder: "अपना संदेश टाइप करें...",
|
||||
clearChatConfirm: "सभी वार्तालाप इतिहास साफ़ करें?",
|
||||
aiAssistantTitle: "AI सहायक",
|
||||
ttsModalTitle: "टेक्स्ट-टू-स्पीच",
|
||||
translateModalTitle: "पृष्ठ अनुवाद",
|
||||
pageContentPrefix: "यहाँ वेबपेज की सामग्री है:",
|
||||
additionalContext:
|
||||
"आप इस पृष्ठ पर एम्बेडेड हैं। प्रश्नों का उत्तर देते समय, पृष्ठ सामग्री और संदर्भ पर विचार करें।",
|
||||
languageChanged:
|
||||
"भाषा {lang} पर सेट की गई। अपनी पसंदीदा भाषा में सभी इंटरफ़ेस तत्वों को देखने के लिए पृष्ठ को रीफ्रेश करें।",
|
||||
translationFailed: "अनुवाद विफल: {error}",
|
||||
ttsFailed: "TTS विफल: {error}",
|
||||
fullChatCopied: "पूर्ण चैट रॉ मार्कडाउन के रूप में कॉपी किया गया!",
|
||||
fullChatHTMLCopied: "पूर्ण चैट HTML के रूप में कॉपी किया गया!",
|
||||
fullPageCopied: "पूर्ण पृष्ठ मार्कडाउन के रूप में कॉपी किया गया!",
|
||||
failedToCopy: "कॉपी करने में विफल: {error}",
|
||||
failedToCopyPage: "पृष्ठ कॉपी करने में विफल: {error}",
|
||||
failedToCopyCode: "कोड कॉपी करने में विफल: {error}",
|
||||
errorOpeningModal: "AI मॉडल खोलने में त्रुटि: {error}",
|
||||
failedToReadPage: "पृष्ठ पढ़ने में विफल: {error}",
|
||||
languageDetectionFailed: "भाषा का पता लगाने में विफल: {error}",
|
||||
currentPage: "वर्तमान पृष्ठ: {lang}",
|
||||
translatingTo: "अनुवाद हो रहा है...",
|
||||
detectingLanguage: "🔍 पता लगाया जा रहा है...",
|
||||
hermesIntro:
|
||||
'nous research के <a href="https://nousresearch.com/hermes3/" target="_blank" style="color: rgba(255,255,255,0.9); text-decoration: underline;">hermes</a> बड़े भाषा मॉडल प्रस्तुत करता है',
|
||||
discussingPage: "आप चर्चा कर रहे हैं",
|
||||
ttsPlaceholder: "टेक्स्ट-टू-स्पीच: कोई भी संदेश लिखें और कृत्रिम बुद्धि इसे बोलने दें!",
|
||||
translatePlaceholder: "अनुवाद के लिए कोई भी टेक्स्ट दर्ज करें...",
|
||||
pleaseEnterText: "कृपया पहले कुछ टेक्स्ट दर्ज करें!",
|
||||
pleaseSelectFile: "कृपया पहले एक फ़ाइल चुनें!",
|
||||
pleaseEnterTranslateText: "कृपया अनुवाद के लिए कुछ टेक्स्ट दर्ज करें!",
|
||||
aiModalNotAvailable: "AI मॉडल इस संदर्भ में उपलब्ध नहीं है",
|
||||
},
|
||||
|
||||
// TODO: Add remaining 16 languages with all keys
|
||||
// For now, other languages will fall back to English
|
||||
|
||||
// Spanish - basic translations (needs complete keys)
|
||||
es: {
|
||||
readPage: "📖 Leer Página",
|
||||
ttsAnything: "🔊 TTS Todo",
|
||||
translate: "🌐 Traducir",
|
||||
smartTranslate: "🤖 Traducción Inteligente",
|
||||
// Additional keys will be added via translation system
|
||||
},
|
||||
|
||||
// French - basic translations (needs complete keys)
|
||||
fr: {
|
||||
readPage: "📖 Lire la Page",
|
||||
ttsAnything: "🔊 TTS Tout",
|
||||
translate: "🌐 Traduire",
|
||||
smartTranslate: "🤖 Traduction Intelligente",
|
||||
// Additional keys will be added via translation system
|
||||
},
|
||||
|
||||
// Additional languages with basic keys...
|
||||
// Full translations for all 19 languages will be added systematically
|
||||
};
|
||||
|
||||
// Get UI text in user's preferred language
|
||||
export function getUIText(key, params = {}) {
|
||||
const lang = getUserLanguagePreference();
|
||||
const translations = UI_TRANSLATIONS[lang] || UI_TRANSLATIONS.en;
|
||||
let text = translations[key] || UI_TRANSLATIONS.en[key] || key;
|
||||
|
||||
// Replace parameters like {lang} with actual values
|
||||
Object.entries(params).forEach(([param, value]) => {
|
||||
text = text.replace(`{${param}}`, value);
|
||||
});
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
// Language preference management
|
||||
export function getUserLanguagePreference() {
|
||||
try {
|
||||
return localStorage.getItem("uncloseai_language") || "en";
|
||||
} catch (error) {
|
||||
console.warn("Failed to read language preference:", error);
|
||||
return "en";
|
||||
}
|
||||
}
|
||||
|
||||
export function setUserLanguagePreference(langCode) {
|
||||
try {
|
||||
localStorage.setItem("uncloseai_language", langCode);
|
||||
console.log("Language preference saved:", langCode);
|
||||
// Trigger UI refresh when language changes
|
||||
if (typeof window !== "undefined" && window.refreshUILanguage) {
|
||||
window.refreshUILanguage();
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("Failed to save language preference:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// Translation function for UI text
|
||||
export async function translateUIText(text, targetLang) {
|
||||
if (targetLang === "en" || !text) return text;
|
||||
|
||||
try {
|
||||
const { translateText } = await import("./translation.js");
|
||||
const translated = await translateText(text, targetLang);
|
||||
return translated || text;
|
||||
} catch (error) {
|
||||
console.warn("UI translation failed:", error);
|
||||
return text;
|
||||
}
|
||||
}
|
||||
908
src/ui.js
908
src/ui.js
|
|
@ -4,794 +4,17 @@ import hljs from "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/es/
|
|||
import { API_KEY, setSystemMessageAppend, TTS_API_URL } from "./config.js";
|
||||
import { extractWebpageContent } from "./content.js";
|
||||
import { speakText } from "./tts.js";
|
||||
|
||||
// Initialize ChunkFive font with absolute path
|
||||
function initializeChunkFiveFont() {
|
||||
const fontFaceStyle = document.createElement("style");
|
||||
fontFaceStyle.textContent = `
|
||||
@font-face {
|
||||
font-family: "ChunkFiveRegular";
|
||||
src: url("https://uncloseai.com/css/chunkfive/chunkfive-regular-webfont.woff2") format("woff2"),
|
||||
url("https://uncloseai.com/css/chunkfive/chunkfive-regular-webfont.woff") format("woff");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(fontFaceStyle);
|
||||
}
|
||||
|
||||
// Fast AI-powered language detection using Hermes
|
||||
async function detectPageLanguage() {
|
||||
try {
|
||||
// Get page content as markdown
|
||||
const pageHtml = document.documentElement.outerHTML;
|
||||
const pageMarkdown = htmlToMarkdown(pageHtml);
|
||||
|
||||
// Take a sample (first 1000 characters) for speed
|
||||
const sample = pageMarkdown.substring(0, 1000);
|
||||
|
||||
// Import required modules
|
||||
const { getSelectedModel, getSelectedModelEndpoint } = await import(
|
||||
"./models.js"
|
||||
);
|
||||
const apiUrl = `${getSelectedModelEndpoint()}/chat/completions`;
|
||||
|
||||
const response = await fetch(apiUrl, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${API_KEY}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: getSelectedModel(),
|
||||
messages: [
|
||||
{
|
||||
role: "system",
|
||||
content:
|
||||
"You are a language classifier. Respond with ONLY the 2-letter ISO language code (en, es, fr, de, etc.) for the detected language. No explanations, just the code.",
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
content: `Detect the language of this text:\n\n${sample}`,
|
||||
},
|
||||
],
|
||||
temperature: 0.1,
|
||||
max_tokens: 10,
|
||||
stream: false,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
console.warn(
|
||||
"Language detection failed, falling back to browser language",
|
||||
);
|
||||
return fallbackLanguageDetection();
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
const detectedLang = data.choices[0].message.content.trim().toLowerCase();
|
||||
|
||||
// Validate it's a reasonable language code (2-3 chars)
|
||||
if (/^[a-z]{2,3}$/.test(detectedLang)) {
|
||||
console.log("AI detected language:", detectedLang);
|
||||
return detectedLang;
|
||||
}
|
||||
console.warn("Invalid language code from AI:", detectedLang);
|
||||
return fallbackLanguageDetection();
|
||||
} catch (error) {
|
||||
console.warn("Language detection error:", error);
|
||||
return fallbackLanguageDetection();
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback language detection using DOM attributes
|
||||
function fallbackLanguageDetection() {
|
||||
// Check html lang attribute first
|
||||
const htmlLang = document.documentElement.lang;
|
||||
if (htmlLang) {
|
||||
const langCode = htmlLang.split("-")[0].toLowerCase();
|
||||
return langCode;
|
||||
}
|
||||
|
||||
// Fallback to browser language
|
||||
const browserLang = navigator.language || navigator.userLanguage;
|
||||
if (browserLang) {
|
||||
const langCode = browserLang.split("-")[0].toLowerCase();
|
||||
return langCode;
|
||||
}
|
||||
|
||||
// Default to English
|
||||
return "en";
|
||||
}
|
||||
|
||||
// Complete UI Translations for all supported languages
|
||||
const UI_TRANSLATIONS = {
|
||||
en: {
|
||||
// Main buttons
|
||||
readPage: "📖 Read Page",
|
||||
ttsAnything: "🔊 TTS Anything",
|
||||
translate: "🌐 Translate",
|
||||
smartTranslate: "🤖 Smart Translate",
|
||||
uploadFile: "📁 Upload File",
|
||||
|
||||
// Settings
|
||||
modelLabel: "AI Model:",
|
||||
voiceLabel: "TTS Voice:",
|
||||
languageLabel: "Interface Language:",
|
||||
quickActions: "Quick Actions:",
|
||||
|
||||
// Actions
|
||||
refreshModels: "🔄 Refresh Models",
|
||||
clearChat: "🗑️ Clear Chat",
|
||||
fullChatRaw: "📋 Full Chat Raw",
|
||||
fullChatHTML: "📄 Full Chat HTML",
|
||||
fullPageRaw: "📝 Full Page Raw",
|
||||
downloadChat: "💾 Download Chat",
|
||||
|
||||
// Chat UI
|
||||
sendButton: "Send",
|
||||
typePlaceholder: "Type your message...",
|
||||
clearChatConfirm: "Clear all conversation history?",
|
||||
|
||||
// Modal headers
|
||||
aiAssistantTitle: "AI Assistant",
|
||||
ttsModalTitle: "Text-to-Speech",
|
||||
translateModalTitle: "Translate Page",
|
||||
|
||||
// System messages
|
||||
pageContentPrefix: "Here's the content of the webpage:",
|
||||
additionalContext:
|
||||
"You are embedded on this page. When answering questions, consider the page content and context.",
|
||||
|
||||
// Notifications and alerts
|
||||
languageChanged:
|
||||
"Language set to {lang}. Refresh the page to see all interface elements in your preferred language.",
|
||||
translationFailed: "Translation failed: {error}",
|
||||
ttsFailed: "TTS failed: {error}",
|
||||
fullChatCopied: "Full chat copied as raw markdown!",
|
||||
fullChatHTMLCopied: "Full chat copied as HTML!",
|
||||
fullPageCopied: "Full page copied as markdown!",
|
||||
failedToCopy: "Failed to copy: {error}",
|
||||
failedToCopyPage: "Failed to copy page: {error}",
|
||||
failedToCopyCode: "Failed to copy code: {error}",
|
||||
errorOpeningModal: "Error opening AI modal: {error}",
|
||||
failedToReadPage: "Failed to read page: {error}",
|
||||
languageDetectionFailed: "Language detection failed: {error}",
|
||||
|
||||
// Dropdown labels
|
||||
currentPage: "Current page: {lang}",
|
||||
translatingTo: "Translating...",
|
||||
detectingLanguage: "🔍 Detecting...",
|
||||
|
||||
// Intro/header text
|
||||
hermesIntro:
|
||||
'presents nous research\'s <a href="https://nousresearch.com/hermes3/" target="_blank" style="color: rgba(255,255,255,0.9); text-decoration: underline;">hermes</a> large language model',
|
||||
discussingPage: "You are discussing",
|
||||
|
||||
// TTS placeholder
|
||||
ttsPlaceholder:
|
||||
"text-to-speech: write any message & have the artificial intelligence speak it!",
|
||||
translatePlaceholder: "Enter any text to translate...",
|
||||
|
||||
// More alerts and messages
|
||||
pleaseEnterText: "Please enter some text first!",
|
||||
pleaseSelectFile: "Please select a file first!",
|
||||
pleaseEnterTranslateText: "Please enter some text to translate!",
|
||||
aiModalNotAvailable: "AI modal not available in this context",
|
||||
},
|
||||
|
||||
// Chinese (Simplified)
|
||||
zh: {
|
||||
readPage: "📖 阅读页面",
|
||||
ttsAnything: "🔊 语音合成",
|
||||
translate: "🌐 翻译",
|
||||
smartTranslate: "🤖 智能翻译",
|
||||
uploadFile: "📁 上传文件",
|
||||
modelLabel: "AI 模型:",
|
||||
voiceLabel: "语音类型:",
|
||||
languageLabel: "界面语言:",
|
||||
quickActions: "快捷操作:",
|
||||
refreshModels: "🔄 刷新模型",
|
||||
clearChat: "🗑️ 清除聊天",
|
||||
fullChatRaw: "📋 完整聊天记录",
|
||||
fullChatHTML: "📄 完整聊天HTML",
|
||||
fullPageRaw: "📝 完整页面原始",
|
||||
downloadChat: "💾 下载聊天",
|
||||
sendButton: "发送",
|
||||
typePlaceholder: "输入您的消息...",
|
||||
clearChatConfirm: "清除所有对话历史记录?",
|
||||
aiAssistantTitle: "AI助手",
|
||||
ttsModalTitle: "文本转语音",
|
||||
translateModalTitle: "翻译页面",
|
||||
pageContentPrefix: "以下是网页内容:",
|
||||
additionalContext: "您已嵌入此页面。回答问题时,请考虑页面内容和上下文。",
|
||||
languageChanged:
|
||||
"语言设置为 {lang}。刷新页面以查看所有界面元素的首选语言。",
|
||||
translationFailed: "翻译失败:{error}",
|
||||
ttsFailed: "语音合成失败:{error}",
|
||||
fullChatCopied: "完整聊天记录已复制为markdown!",
|
||||
fullChatHTMLCopied: "完整聊天记录已复制为HTML!",
|
||||
fullPageCopied: "完整页面已复制为markdown!",
|
||||
failedToCopy: "复制失败:{error}",
|
||||
failedToCopyPage: "复制页面失败:{error}",
|
||||
failedToCopyCode: "复制代码失败:{error}",
|
||||
errorOpeningModal: "打开AI模态框时出错:{error}",
|
||||
failedToReadPage: "读取页面失败:{error}",
|
||||
languageDetectionFailed: "语言检测失败:{error}",
|
||||
currentPage: "当前页面:{lang}",
|
||||
translatingTo: "翻译中...",
|
||||
detectingLanguage: "🔍 检测中...",
|
||||
hermesIntro:
|
||||
'呈现nous research的<a href="https://nousresearch.com/hermes3/" target="_blank" style="color: rgba(255,255,255,0.9); text-decoration: underline;">hermes</a>大型语言模型',
|
||||
discussingPage: "您正在讨论",
|
||||
ttsPlaceholder: "文本转语音:写任何消息,让人工智能朗读出来!",
|
||||
translatePlaceholder: "输入任何要翻译的文本...",
|
||||
pleaseEnterText: "请先输入一些文本!",
|
||||
pleaseSelectFile: "请先选择一个文件!",
|
||||
pleaseEnterTranslateText: "请输入一些要翻译的文本!",
|
||||
aiModalNotAvailable: "AI模态框在此上下文中不可用",
|
||||
},
|
||||
|
||||
// Hindi
|
||||
hi: {
|
||||
readPage: "📖 पृष्ठ पढ़ें",
|
||||
ttsAnything: "🔊 TTS कुछ भी",
|
||||
translate: "🌐 अनुवाद करें",
|
||||
smartTranslate: "🤖 स्मार्ट अनुवाद",
|
||||
uploadFile: "📁 फ़ाइल अपलोड करें",
|
||||
modelLabel: "AI मॉडल:",
|
||||
voiceLabel: "TTS आवाज़:",
|
||||
languageLabel: "इंटरफ़ेस भाषा:",
|
||||
quickActions: "त्वरित क्रियाएं:",
|
||||
refreshModels: "🔄 मॉडल रीफ्रेश करें",
|
||||
clearChat: "🗑️ चैट साफ़ करें",
|
||||
fullChatRaw: "📋 पूर्ण चैट रॉ",
|
||||
downloadChat: "💾 चैट डाउनलोड करें",
|
||||
sendButton: "भेजें",
|
||||
typePlaceholder: "अपना संदेश टाइप करें...",
|
||||
clearChatConfirm: "सभी वार्तालाप इतिहास साफ़ करें?",
|
||||
pageContentPrefix: "यहाँ वेबपेज की सामग्री है:",
|
||||
additionalContext:
|
||||
"आप इस पृष्ठ पर एम्बेडेड हैं। प्रश्नों का उत्तर देते समय, पृष्ठ सामग्री और संदर्भ पर विचार करें।",
|
||||
languageChanged:
|
||||
"भाषा {lang} पर सेट की गई। अपनी पसंदीदा भाषा में सभी इंटरफ़ेस तत्वों को देखने के लिए पृष्ठ को रीफ्रेश करें।",
|
||||
translationFailed: "अनुवाद विफल: {error}",
|
||||
ttsFailed: "TTS विफल: {error}",
|
||||
currentPage: "वर्तमान पृष्ठ: {lang}",
|
||||
translatingTo: "अनुवाद हो रहा है...",
|
||||
detectingLanguage: "🔍 पता लगाया जा रहा है...",
|
||||
},
|
||||
|
||||
// Spanish
|
||||
es: {
|
||||
readPage: "📖 Leer Página",
|
||||
ttsAnything: "🔊 TTS Todo",
|
||||
translate: "🌐 Traducir",
|
||||
smartTranslate: "🤖 Traducción Inteligente",
|
||||
uploadFile: "📁 Subir Archivo",
|
||||
modelLabel: "Modelo de IA:",
|
||||
voiceLabel: "Voz TTS:",
|
||||
languageLabel: "Idioma de la Interfaz:",
|
||||
quickActions: "Acciones Rápidas:",
|
||||
refreshModels: "🔄 Actualizar Modelos",
|
||||
clearChat: "🗑️ Borrar Chat",
|
||||
fullChatRaw: "📋 Chat Completo Raw",
|
||||
downloadChat: "💾 Descargar Chat",
|
||||
sendButton: "Enviar",
|
||||
typePlaceholder: "Escribe tu mensaje...",
|
||||
clearChatConfirm: "¿Borrar todo el historial de conversación?",
|
||||
pageContentPrefix: "Aquí está el contenido de la página web:",
|
||||
additionalContext:
|
||||
"Estás integrado en esta página. Al responder preguntas, considera el contenido y contexto de la página.",
|
||||
languageChanged:
|
||||
"Idioma establecido en {lang}. Actualiza la página para ver todos los elementos en tu idioma preferido.",
|
||||
translationFailed: "Error en traducción: {error}",
|
||||
ttsFailed: "Error en TTS: {error}",
|
||||
currentPage: "Página actual: {lang}",
|
||||
translatingTo: "Traduciendo...",
|
||||
detectingLanguage: "🔍 Detectando...",
|
||||
},
|
||||
|
||||
// French
|
||||
fr: {
|
||||
readPage: "📖 Lire la Page",
|
||||
ttsAnything: "🔊 TTS Tout",
|
||||
translate: "🌐 Traduire",
|
||||
smartTranslate: "🤖 Traduction Intelligente",
|
||||
uploadFile: "📁 Télécharger Fichier",
|
||||
modelLabel: "Modèle IA :",
|
||||
voiceLabel: "Voix TTS :",
|
||||
languageLabel: "Langue de l'Interface :",
|
||||
quickActions: "Actions Rapides :",
|
||||
refreshModels: "🔄 Actualiser les Modèles",
|
||||
clearChat: "🗑️ Effacer le Chat",
|
||||
fullChatRaw: "📋 Chat Complet Brut",
|
||||
downloadChat: "💾 Télécharger le Chat",
|
||||
sendButton: "Envoyer",
|
||||
typePlaceholder: "Tapez votre message...",
|
||||
clearChatConfirm: "Effacer tout l'historique de conversation ?",
|
||||
pageContentPrefix: "Voici le contenu de la page web :",
|
||||
additionalContext:
|
||||
"Vous êtes intégré sur cette page. Lors de la réponse aux questions, considérez le contenu et le contexte de la page.",
|
||||
languageChanged:
|
||||
"Langue définie sur {lang}. Actualisez la page pour voir tous les éléments de l'interface dans votre langue préférée.",
|
||||
translationFailed: "Échec de la traduction : {error}",
|
||||
ttsFailed: "Échec du TTS : {error}",
|
||||
currentPage: "Page actuelle : {lang}",
|
||||
translatingTo: "Traduction en cours...",
|
||||
detectingLanguage: "🔍 Détection...",
|
||||
},
|
||||
|
||||
// Arabic
|
||||
ar: {
|
||||
readPage: "📖 قراءة الصفحة",
|
||||
ttsAnything: "🔊 تحويل النص إلى كلام",
|
||||
translate: "🌐 ترجمة",
|
||||
smartTranslate: "🤖 ترجمة ذكية",
|
||||
uploadFile: "📁 رفع ملف",
|
||||
modelLabel: "نموذج الذكاء الاصطناعي:",
|
||||
voiceLabel: "صوت TTS:",
|
||||
languageLabel: "لغة الواجهة:",
|
||||
quickActions: "إجراءات سريعة:",
|
||||
refreshModels: "🔄 تحديث النماذج",
|
||||
clearChat: "🗑️ مسح المحادثة",
|
||||
fullChatRaw: "📋 المحادثة الكاملة",
|
||||
downloadChat: "💾 تحميل المحادثة",
|
||||
sendButton: "إرسال",
|
||||
typePlaceholder: "اكتب رسالتك...",
|
||||
clearChatConfirm: "مسح كل سجل المحادثة؟",
|
||||
pageContentPrefix: "هنا محتوى صفحة الويب:",
|
||||
additionalContext:
|
||||
"أنت مضمن في هذه الصفحة. عند الإجابة على الأسئلة، ضع في اعتبارك محتوى الصفحة والسياق.",
|
||||
languageChanged:
|
||||
"تم تعيين اللغة إلى {lang}. قم بتحديث الصفحة لرؤية جميع عناصر الواجهة بلغتك المفضلة.",
|
||||
translationFailed: "فشلت الترجمة: {error}",
|
||||
ttsFailed: "فشل TTS: {error}",
|
||||
currentPage: "الصفحة الحالية: {lang}",
|
||||
translatingTo: "جارٍ الترجمة...",
|
||||
detectingLanguage: "🔍 جارٍ الكشف...",
|
||||
},
|
||||
|
||||
// Bengali
|
||||
bn: {
|
||||
readPage: "📖 পৃষ্ঠা পড়ুন",
|
||||
ttsAnything: "🔊 TTS সবকিছু",
|
||||
translate: "🌐 অনুবাদ",
|
||||
smartTranslate: "🤖 স্মার্ট অনুবাদ",
|
||||
uploadFile: "📁 ফাইল আপলোড",
|
||||
modelLabel: "AI মডেল:",
|
||||
voiceLabel: "TTS ভয়েস:",
|
||||
languageLabel: "ইন্টারফেস ভাষা:",
|
||||
quickActions: "দ্রুত ক্রিয়া:",
|
||||
refreshModels: "🔄 মডেল রিফ্রেশ",
|
||||
clearChat: "🗑️ চ্যাট মুছুন",
|
||||
fullChatRaw: "📋 সম্পূর্ণ চ্যাট র",
|
||||
downloadChat: "💾 চ্যাট ডাউনলোড",
|
||||
sendButton: "পাঠান",
|
||||
typePlaceholder: "আপনার বার্তা টাইপ করুন...",
|
||||
clearChatConfirm: "সমস্ত কথোপকথনের ইতিহাস মুছে ফেলবেন?",
|
||||
pageContentPrefix: "এখানে ওয়েবপেজের বিষয়বস্তু:",
|
||||
additionalContext:
|
||||
"আপনি এই পৃষ্ঠায় এম্বেড করা আছেন। প্রশ্নের উত্তর দেওয়ার সময়, পৃষ্ঠার বিষয়বস্তু এবং প্রসঙ্গ বিবেচনা করুন।",
|
||||
languageChanged:
|
||||
"ভাষা {lang} সেট করা হয়েছে। আপনার পছন্দের ভাষায় সমস্ত ইন্টারফেস উপাদান দেখতে পৃষ্ঠাটি রিফ্রেশ করুন।",
|
||||
translationFailed: "অনুবাদ ব্যর্থ: {error}",
|
||||
ttsFailed: "TTS ব্যর্থ: {error}",
|
||||
currentPage: "বর্তমান পৃষ্ঠা: {lang}",
|
||||
translatingTo: "অনুবাদ হচ্ছে...",
|
||||
detectingLanguage: "🔍 সনাক্ত করা হচ্ছে...",
|
||||
},
|
||||
|
||||
// Russian
|
||||
ru: {
|
||||
readPage: "📖 Читать Страницу",
|
||||
ttsAnything: "🔊 TTS Всё",
|
||||
translate: "🌐 Перевести",
|
||||
smartTranslate: "🤖 Умный Перевод",
|
||||
uploadFile: "📁 Загрузить Файл",
|
||||
modelLabel: "Модель ИИ:",
|
||||
voiceLabel: "Голос TTS:",
|
||||
languageLabel: "Язык Интерфейса:",
|
||||
quickActions: "Быстрые Действия:",
|
||||
refreshModels: "🔄 Обновить Модели",
|
||||
clearChat: "🗑️ Очистить Чат",
|
||||
fullChatRaw: "📋 Полный Чат Raw",
|
||||
downloadChat: "💾 Скачать Чат",
|
||||
sendButton: "Отправить",
|
||||
typePlaceholder: "Введите ваше сообщение...",
|
||||
clearChatConfirm: "Очистить всю историю разговора?",
|
||||
pageContentPrefix: "Вот содержимое веб-страницы:",
|
||||
additionalContext:
|
||||
"Вы встроены на эту страницу. При ответе на вопросы учитывайте содержание и контекст страницы.",
|
||||
languageChanged:
|
||||
"Язык установлен на {lang}. Обновите страницу, чтобы увидеть все элементы интерфейса на предпочитаемом языке.",
|
||||
translationFailed: "Ошибка перевода: {error}",
|
||||
ttsFailed: "Ошибка TTS: {error}",
|
||||
currentPage: "Текущая страница: {lang}",
|
||||
translatingTo: "Перевод...",
|
||||
detectingLanguage: "🔍 Определение...",
|
||||
},
|
||||
|
||||
// Portuguese
|
||||
pt: {
|
||||
readPage: "📖 Ler Página",
|
||||
ttsAnything: "🔊 TTS Tudo",
|
||||
translate: "🌐 Traduzir",
|
||||
smartTranslate: "🤖 Tradução Inteligente",
|
||||
uploadFile: "📁 Enviar Arquivo",
|
||||
modelLabel: "Modelo de IA:",
|
||||
voiceLabel: "Voz TTS:",
|
||||
languageLabel: "Idioma da Interface:",
|
||||
quickActions: "Ações Rápidas:",
|
||||
refreshModels: "🔄 Atualizar Modelos",
|
||||
clearChat: "🗑️ Limpar Chat",
|
||||
fullChatRaw: "📋 Chat Completo Raw",
|
||||
downloadChat: "💾 Baixar Chat",
|
||||
sendButton: "Enviar",
|
||||
typePlaceholder: "Digite sua mensagem...",
|
||||
clearChatConfirm: "Limpar todo o histórico de conversas?",
|
||||
pageContentPrefix: "Aqui está o conteúdo da página web:",
|
||||
additionalContext:
|
||||
"Você está incorporado nesta página. Ao responder perguntas, considere o conteúdo e contexto da página.",
|
||||
languageChanged:
|
||||
"Idioma definido para {lang}. Atualize a página para ver todos os elementos da interface no seu idioma preferido.",
|
||||
translationFailed: "Falha na tradução: {error}",
|
||||
ttsFailed: "Falha no TTS: {error}",
|
||||
currentPage: "Página atual: {lang}",
|
||||
translatingTo: "Traduzindo...",
|
||||
detectingLanguage: "🔍 Detectando...",
|
||||
},
|
||||
|
||||
// Urdu
|
||||
ur: {
|
||||
readPage: "📖 صفحہ پڑھیں",
|
||||
ttsAnything: "🔊 TTS سب کچھ",
|
||||
translate: "🌐 ترجمہ",
|
||||
smartTranslate: "🤖 سمارٹ ترجمہ",
|
||||
uploadFile: "📁 فائل اپ لوڈ",
|
||||
modelLabel: "AI ماڈل:",
|
||||
voiceLabel: "TTS آواز:",
|
||||
languageLabel: "انٹرفیس زبان:",
|
||||
quickActions: "فوری اعمال:",
|
||||
refreshModels: "🔄 ماڈلز ریفریش",
|
||||
clearChat: "🗑️ چیٹ صاف کریں",
|
||||
fullChatRaw: "📋 مکمل چیٹ Raw",
|
||||
downloadChat: "💾 چیٹ ڈاؤن لوڈ",
|
||||
sendButton: "بھیجیں",
|
||||
typePlaceholder: "اپنا پیغام ٹائپ کریں...",
|
||||
clearChatConfirm: "تمام گفتگو کی تاریخ صاف کریں؟",
|
||||
pageContentPrefix: "یہاں ویب پیج کا مواد ہے:",
|
||||
additionalContext:
|
||||
"آپ اس صفحے پر ایمبیڈ ہیں۔ سوالات کا جواب دیتے وقت، صفحے کے مواد اور سیاق و سباق پر غور کریں۔",
|
||||
languageChanged:
|
||||
"زبان {lang} پر سیٹ کی گئی۔ اپنی پسندیدہ زبان میں تمام انٹرفیس عناصر دیکھنے کے لیے صفحہ ریفریش کریں۔",
|
||||
translationFailed: "ترجمہ ناکام: {error}",
|
||||
ttsFailed: "TTS ناکام: {error}",
|
||||
currentPage: "موجودہ صفحہ: {lang}",
|
||||
translatingTo: "ترجمہ ہو رہا ہے...",
|
||||
detectingLanguage: "🔍 تلاش کر رہے ہیں...",
|
||||
},
|
||||
|
||||
// Indonesian
|
||||
id: {
|
||||
readPage: "📖 Baca Halaman",
|
||||
ttsAnything: "🔊 TTS Semua",
|
||||
translate: "🌐 Terjemahkan",
|
||||
smartTranslate: "🤖 Terjemahan Cerdas",
|
||||
uploadFile: "📁 Unggah Berkas",
|
||||
modelLabel: "Model AI:",
|
||||
voiceLabel: "Suara TTS:",
|
||||
languageLabel: "Bahasa Antarmuka:",
|
||||
quickActions: "Aksi Cepat:",
|
||||
refreshModels: "🔄 Segarkan Model",
|
||||
clearChat: "🗑️ Hapus Obrolan",
|
||||
fullChatRaw: "📋 Obrolan Lengkap Raw",
|
||||
downloadChat: "💾 Unduh Obrolan",
|
||||
sendButton: "Kirim",
|
||||
typePlaceholder: "Ketik pesan Anda...",
|
||||
clearChatConfirm: "Hapus semua riwayat percakapan?",
|
||||
pageContentPrefix: "Berikut adalah konten halaman web:",
|
||||
additionalContext:
|
||||
"Anda tertanam di halaman ini. Saat menjawab pertanyaan, pertimbangkan konten dan konteks halaman.",
|
||||
languageChanged:
|
||||
"Bahasa diatur ke {lang}. Segarkan halaman untuk melihat semua elemen antarmuka dalam bahasa pilihan Anda.",
|
||||
translationFailed: "Terjemahan gagal: {error}",
|
||||
ttsFailed: "TTS gagal: {error}",
|
||||
currentPage: "Halaman saat ini: {lang}",
|
||||
translatingTo: "Menerjemahkan...",
|
||||
detectingLanguage: "🔍 Mendeteksi...",
|
||||
},
|
||||
|
||||
// German
|
||||
de: {
|
||||
readPage: "📖 Seite Lesen",
|
||||
ttsAnything: "🔊 TTS Alles",
|
||||
translate: "🌐 Übersetzen",
|
||||
smartTranslate: "🤖 Intelligente Übersetzung",
|
||||
uploadFile: "📁 Datei Hochladen",
|
||||
modelLabel: "KI-Modell:",
|
||||
voiceLabel: "TTS-Stimme:",
|
||||
languageLabel: "Oberflächensprache:",
|
||||
quickActions: "Schnellaktionen:",
|
||||
refreshModels: "🔄 Modelle Aktualisieren",
|
||||
clearChat: "🗑️ Chat Löschen",
|
||||
fullChatRaw: "📋 Vollständiger Chat Raw",
|
||||
downloadChat: "💾 Chat Herunterladen",
|
||||
sendButton: "Senden",
|
||||
typePlaceholder: "Geben Sie Ihre Nachricht ein...",
|
||||
clearChatConfirm: "Gesamten Gesprächsverlauf löschen?",
|
||||
pageContentPrefix: "Hier ist der Inhalt der Webseite:",
|
||||
additionalContext:
|
||||
"Sie sind auf dieser Seite eingebettet. Berücksichtigen Sie bei der Beantwortung von Fragen den Inhalt und Kontext der Seite.",
|
||||
languageChanged:
|
||||
"Sprache auf {lang} eingestellt. Aktualisieren Sie die Seite, um alle Oberflächenelemente in Ihrer bevorzugten Sprache zu sehen.",
|
||||
translationFailed: "Übersetzung fehlgeschlagen: {error}",
|
||||
ttsFailed: "TTS fehlgeschlagen: {error}",
|
||||
currentPage: "Aktuelle Seite: {lang}",
|
||||
translatingTo: "Übersetze...",
|
||||
detectingLanguage: "🔍 Erkenne...",
|
||||
},
|
||||
|
||||
// Japanese
|
||||
ja: {
|
||||
readPage: "📖 ページを読む",
|
||||
ttsAnything: "🔊 TTS すべて",
|
||||
translate: "🌐 翻訳",
|
||||
smartTranslate: "🤖 スマート翻訳",
|
||||
uploadFile: "📁 ファイルをアップロード",
|
||||
modelLabel: "AIモデル:",
|
||||
voiceLabel: "TTS音声:",
|
||||
languageLabel: "インターフェース言語:",
|
||||
quickActions: "クイックアクション:",
|
||||
refreshModels: "🔄 モデルを更新",
|
||||
clearChat: "🗑️ チャットをクリア",
|
||||
fullChatRaw: "📋 完全なチャットRaw",
|
||||
downloadChat: "💾 チャットをダウンロード",
|
||||
sendButton: "送信",
|
||||
typePlaceholder: "メッセージを入力してください...",
|
||||
clearChatConfirm: "すべての会話履歴をクリアしますか?",
|
||||
pageContentPrefix: "ウェブページのコンテンツは次のとおりです:",
|
||||
additionalContext:
|
||||
"このページに埋め込まれています。質問に答える際は、ページのコンテンツとコンテキストを考慮してください。",
|
||||
languageChanged:
|
||||
"言語を{lang}に設定しました。すべてのインターフェース要素を希望の言語で表示するには、ページを更新してください。",
|
||||
translationFailed: "翻訳失敗:{error}",
|
||||
ttsFailed: "TTS失敗:{error}",
|
||||
currentPage: "現在のページ:{lang}",
|
||||
translatingTo: "翻訳中...",
|
||||
detectingLanguage: "🔍 検出中...",
|
||||
},
|
||||
|
||||
// Swahili
|
||||
sw: {
|
||||
readPage: "📖 Soma Ukurasa",
|
||||
ttsAnything: "🔊 TTS Yote",
|
||||
translate: "🌐 Tafsiri",
|
||||
smartTranslate: "🤖 Tafsiri ya Akili",
|
||||
uploadFile: "📁 Pakia Faili",
|
||||
modelLabel: "Mfano wa AI:",
|
||||
voiceLabel: "Sauti ya TTS:",
|
||||
languageLabel: "Lugha ya Kiolesura:",
|
||||
quickActions: "Vitendo vya Haraka:",
|
||||
refreshModels: "🔄 Onyesha Upya Mifano",
|
||||
clearChat: "🗑️ Futa Mazungumzo",
|
||||
fullChatRaw: "📋 Mazungumzo Kamili Raw",
|
||||
downloadChat: "💾 Pakua Mazungumzo",
|
||||
sendButton: "Tuma",
|
||||
typePlaceholder: "Andika ujumbe wako...",
|
||||
clearChatConfirm: "Futa historia yote ya mazungumzo?",
|
||||
pageContentPrefix: "Hapa kuna maudhui ya ukurasa wa wavuti:",
|
||||
additionalContext:
|
||||
"Umepachikwa kwenye ukurasa huu. Unapojibu maswali, zingatia maudhui na muktadha wa ukurasa.",
|
||||
languageChanged:
|
||||
"Lugha imewekwa kuwa {lang}. Onyesha upya ukurasa ili kuona vipengele vyote vya kiolesura katika lugha unayopendelea.",
|
||||
translationFailed: "Tafsiri imeshindwa: {error}",
|
||||
ttsFailed: "TTS imeshindwa: {error}",
|
||||
currentPage: "Ukurasa wa sasa: {lang}",
|
||||
translatingTo: "Inatafsiri...",
|
||||
detectingLanguage: "🔍 Inatambua...",
|
||||
},
|
||||
|
||||
// Marathi
|
||||
mr: {
|
||||
readPage: "📖 पृष्ठ वाचा",
|
||||
ttsAnything: "🔊 TTS सर्व",
|
||||
translate: "🌐 भाषांतर",
|
||||
smartTranslate: "🤖 स्मार्ट भाषांतर",
|
||||
uploadFile: "📁 फाईल अपलोड",
|
||||
modelLabel: "AI मॉडेल:",
|
||||
voiceLabel: "TTS आवाज:",
|
||||
languageLabel: "इंटरफेस भाषा:",
|
||||
quickActions: "जलद क्रिया:",
|
||||
refreshModels: "🔄 मॉडेल रीफ्रेश",
|
||||
clearChat: "🗑️ चॅट साफ करा",
|
||||
fullChatRaw: "📋 संपूर्ण चॅट Raw",
|
||||
downloadChat: "💾 चॅट डाउनलोड",
|
||||
sendButton: "पाठवा",
|
||||
typePlaceholder: "तुमचा संदेश टाइप करा...",
|
||||
clearChatConfirm: "सर्व संभाषण इतिहास साफ करायचा?",
|
||||
pageContentPrefix: "येथे वेबपेजची सामग्री आहे:",
|
||||
additionalContext:
|
||||
"तुम्ही या पृष्ठावर एम्बेड केलेले आहात. प्रश्नांची उत्तरे देताना, पृष्ठाची सामग्री आणि संदर्भ विचारात घ्या.",
|
||||
languageChanged:
|
||||
"भाषा {lang} वर सेट केली. तुमच्या पसंतीच्या भाषेत सर्व इंटरफेस घटक पाहण्यासाठी पृष्ठ रीफ्रेश करा.",
|
||||
translationFailed: "भाषांतर अयशस्वी: {error}",
|
||||
ttsFailed: "TTS अयशस्वी: {error}",
|
||||
currentPage: "वर्तमान पृष्ठ: {lang}",
|
||||
translatingTo: "भाषांतर करत आहे...",
|
||||
detectingLanguage: "🔍 शोधत आहे...",
|
||||
},
|
||||
|
||||
// Telugu
|
||||
te: {
|
||||
readPage: "📖 పేజీ చదవండి",
|
||||
ttsAnything: "🔊 TTS అన్నీ",
|
||||
translate: "🌐 అనువదించు",
|
||||
smartTranslate: "🤖 స్మార్ట్ అనువాదం",
|
||||
uploadFile: "📁 ఫైల్ అప్లోడ్",
|
||||
modelLabel: "AI మోడల్:",
|
||||
voiceLabel: "TTS వాయిస్:",
|
||||
languageLabel: "ఇంటర్ఫేస్ భాష:",
|
||||
quickActions: "త్వరిత చర్యలు:",
|
||||
refreshModels: "🔄 మోడల్స్ రిఫ్రెష్",
|
||||
clearChat: "🗑️ చాట్ క్లియర్",
|
||||
fullChatRaw: "📋 పూర్తి చాట్ Raw",
|
||||
downloadChat: "💾 చాట్ డౌన్లోడ్",
|
||||
sendButton: "పంపు",
|
||||
typePlaceholder: "మీ సందేశం టైప్ చేయండి...",
|
||||
clearChatConfirm: "అన్ని సంభాషణ చరిత్రను క్లియర్ చేయాలా?",
|
||||
pageContentPrefix: "వెబ్పేజీ కంటెంట్ ఇక్కడ ఉంది:",
|
||||
additionalContext:
|
||||
"మీరు ఈ పేజీలో ఎంబెడ్ చేయబడ్డారు. ప్రశ్నలకు సమాధానం ఇచ్చేటప్పుడు, పేజీ కంటెంట్ మరియు సందర్భాన్ని పరిగణించండి.",
|
||||
languageChanged:
|
||||
"భాష {lang}కి సెట్ చేయబడింది. మీ ప్రాధాన్య భాషలో అన్ని ఇంటర్ఫేస్ అంశాలను చూడటానికి పేజీని రిఫ్రెష్ చేయండి.",
|
||||
translationFailed: "అనువాదం విఫలమైంది: {error}",
|
||||
ttsFailed: "TTS విఫలమైంది: {error}",
|
||||
currentPage: "ప్రస్తుత పేజీ: {lang}",
|
||||
translatingTo: "అనువదిస్తోంది...",
|
||||
detectingLanguage: "🔍 గుర్తిస్తోంది...",
|
||||
},
|
||||
|
||||
// Turkish
|
||||
tr: {
|
||||
readPage: "📖 Sayfayı Oku",
|
||||
ttsAnything: "🔊 TTS Her Şey",
|
||||
translate: "🌐 Çevir",
|
||||
smartTranslate: "🤖 Akıllı Çeviri",
|
||||
uploadFile: "📁 Dosya Yükle",
|
||||
modelLabel: "AI Modeli:",
|
||||
voiceLabel: "TTS Sesi:",
|
||||
languageLabel: "Arayüz Dili:",
|
||||
quickActions: "Hızlı İşlemler:",
|
||||
refreshModels: "🔄 Modelleri Yenile",
|
||||
clearChat: "🗑️ Sohbeti Temizle",
|
||||
fullChatRaw: "📋 Tam Sohbet Raw",
|
||||
downloadChat: "💾 Sohbeti İndir",
|
||||
sendButton: "Gönder",
|
||||
typePlaceholder: "Mesajınızı yazın...",
|
||||
clearChatConfirm: "Tüm konuşma geçmişi silinsin mi?",
|
||||
pageContentPrefix: "Web sayfasının içeriği burada:",
|
||||
additionalContext:
|
||||
"Bu sayfaya gömülüsünüz. Soruları yanıtlarken, sayfa içeriğini ve bağlamını göz önünde bulundurun.",
|
||||
languageChanged:
|
||||
"Dil {lang} olarak ayarlandı. Tüm arayüz öğelerini tercih ettiğiniz dilde görmek için sayfayı yenileyin.",
|
||||
translationFailed: "Çeviri başarısız: {error}",
|
||||
ttsFailed: "TTS başarısız: {error}",
|
||||
currentPage: "Geçerli sayfa: {lang}",
|
||||
translatingTo: "Çevriliyor...",
|
||||
detectingLanguage: "🔍 Algılanıyor...",
|
||||
},
|
||||
|
||||
// Chinese (Traditional)
|
||||
"zh-tw": {
|
||||
readPage: "📖 閱讀頁面",
|
||||
ttsAnything: "🔊 語音合成",
|
||||
translate: "🌐 翻譯",
|
||||
smartTranslate: "🤖 智慧翻譯",
|
||||
uploadFile: "📁 上傳檔案",
|
||||
modelLabel: "AI 模型:",
|
||||
voiceLabel: "語音類型:",
|
||||
languageLabel: "介面語言:",
|
||||
quickActions: "快速操作:",
|
||||
refreshModels: "🔄 重新整理模型",
|
||||
clearChat: "🗑️ 清除聊天",
|
||||
fullChatRaw: "📋 完整聊天記錄",
|
||||
downloadChat: "💾 下載聊天",
|
||||
sendButton: "傳送",
|
||||
typePlaceholder: "輸入您的訊息...",
|
||||
clearChatConfirm: "清除所有對話歷史記錄?",
|
||||
pageContentPrefix: "以下是網頁內容:",
|
||||
additionalContext: "您已嵌入此頁面。回答問題時,請考慮頁面內容和上下文。",
|
||||
languageChanged:
|
||||
"語言設定為 {lang}。重新整理頁面以查看所有介面元素的首選語言。",
|
||||
translationFailed: "翻譯失敗:{error}",
|
||||
ttsFailed: "語音合成失敗:{error}",
|
||||
currentPage: "目前頁面:{lang}",
|
||||
translatingTo: "翻譯中...",
|
||||
detectingLanguage: "🔍 偵測中...",
|
||||
},
|
||||
|
||||
// Korean
|
||||
ko: {
|
||||
readPage: "📖 페이지 읽기",
|
||||
ttsAnything: "🔊 TTS 모두",
|
||||
translate: "🌐 번역",
|
||||
smartTranslate: "🤖 스마트 번역",
|
||||
uploadFile: "📁 파일 업로드",
|
||||
modelLabel: "AI 모델:",
|
||||
voiceLabel: "TTS 음성:",
|
||||
languageLabel: "인터페이스 언어:",
|
||||
quickActions: "빠른 작업:",
|
||||
refreshModels: "🔄 모델 새로고침",
|
||||
clearChat: "🗑️ 채팅 지우기",
|
||||
fullChatRaw: "📋 전체 채팅 Raw",
|
||||
downloadChat: "💾 채팅 다운로드",
|
||||
sendButton: "보내기",
|
||||
typePlaceholder: "메시지를 입력하세요...",
|
||||
clearChatConfirm: "모든 대화 기록을 지우시겠습니까?",
|
||||
pageContentPrefix: "웹페이지 내용은 다음과 같습니다:",
|
||||
additionalContext:
|
||||
"이 페이지에 포함되어 있습니다. 질문에 답할 때 페이지 내용과 맥락을 고려하세요.",
|
||||
languageChanged:
|
||||
"언어가 {lang}(으)로 설정되었습니다. 모든 인터페이스 요소를 선호하는 언어로 보려면 페이지를 새로고침하세요.",
|
||||
translationFailed: "번역 실패: {error}",
|
||||
ttsFailed: "TTS 실패: {error}",
|
||||
currentPage: "현재 페이지: {lang}",
|
||||
translatingTo: "번역 중...",
|
||||
detectingLanguage: "🔍 감지 중...",
|
||||
},
|
||||
};
|
||||
|
||||
// Get UI text in user's preferred language
|
||||
function getUIText(key, params = {}) {
|
||||
const lang = getUserLanguagePreference();
|
||||
const translations = UI_TRANSLATIONS[lang] || UI_TRANSLATIONS.en;
|
||||
let text = translations[key] || UI_TRANSLATIONS.en[key] || key;
|
||||
|
||||
// Replace parameters like {lang} with actual values
|
||||
Object.entries(params).forEach(([param, value]) => {
|
||||
text = text.replace(`{${param}}`, value);
|
||||
});
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
// Language preference management
|
||||
function getUserLanguagePreference() {
|
||||
try {
|
||||
return localStorage.getItem("uncloseai_language") || "en";
|
||||
} catch (error) {
|
||||
console.warn("Failed to read language preference:", error);
|
||||
return "en";
|
||||
}
|
||||
}
|
||||
|
||||
function setUserLanguagePreference(langCode) {
|
||||
try {
|
||||
localStorage.setItem("uncloseai_language", langCode);
|
||||
console.log("Language preference saved:", langCode);
|
||||
// Trigger UI refresh when language changes
|
||||
refreshUILanguage();
|
||||
} catch (error) {
|
||||
console.warn("Failed to save language preference:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// Translation function for UI text
|
||||
async function translateUIText(text, targetLang) {
|
||||
if (targetLang === "en" || !text) return text;
|
||||
|
||||
try {
|
||||
const { translateText } = await import("./translation.js");
|
||||
const translated = await translateText(text, targetLang);
|
||||
return translated || text;
|
||||
} catch (error) {
|
||||
console.warn("UI translation failed:", error);
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
import {
|
||||
getUIText,
|
||||
getUserLanguagePreference,
|
||||
setUserLanguagePreference,
|
||||
} from "./ui-translations.js";
|
||||
import {
|
||||
detectCurrentTheme,
|
||||
getThemeColors,
|
||||
initializeChunkFiveFont,
|
||||
} from "./ui-themes.js";
|
||||
import { detectPageLanguage } from "./language-detection.js";
|
||||
// Refresh UI elements when language preference changes
|
||||
function refreshUILanguage() {
|
||||
console.log("UI language refresh triggered");
|
||||
|
|
@ -833,100 +56,6 @@ function refreshUILanguage() {
|
|||
if (langLabel) langLabel.textContent = getUIText("languageLabel");
|
||||
}
|
||||
|
||||
// Theme detection and styling functions
|
||||
function detectCurrentTheme() {
|
||||
const isDark =
|
||||
document.documentElement.getAttribute("data-theme") === "dark" ||
|
||||
(window.matchMedia?.("(prefers-color-scheme: dark)").matches &&
|
||||
!document.documentElement.getAttribute("data-theme"));
|
||||
|
||||
return isDark ? "dark" : "light";
|
||||
}
|
||||
|
||||
function getThemeColors(theme = detectCurrentTheme()) {
|
||||
if (theme === "dark") {
|
||||
return {
|
||||
// Main backgrounds
|
||||
modalBackground: "#1a1a1a",
|
||||
contentBackground: "#2d2d2d",
|
||||
panelBackground: "#262626",
|
||||
inputBackground: "#363636",
|
||||
|
||||
// Text colors
|
||||
primaryText: "#ffffff",
|
||||
secondaryText: "#b3b3b3",
|
||||
mutedText: "#808080",
|
||||
|
||||
// Borders and dividers
|
||||
borderColor: "#404040",
|
||||
dividerColor: "#333333",
|
||||
|
||||
// Interactive elements
|
||||
buttonBackground: "#404040",
|
||||
buttonHover: "#4a4a4a",
|
||||
buttonBorder: "#555555",
|
||||
|
||||
// Message bubbles
|
||||
userMessageBg: "#4a5568",
|
||||
userMessageText: "#ffffff",
|
||||
aiMessageBg: "#363636",
|
||||
aiMessageText: "#ffffff",
|
||||
|
||||
// Input focus
|
||||
focusBorder: "#667eea",
|
||||
|
||||
// Shadows (adjusted for dark theme)
|
||||
shadowColor: "rgba(0,0,0,0.5)",
|
||||
lightShadow: "rgba(0,0,0,0.3)",
|
||||
|
||||
// Action buttons
|
||||
actionBg: "rgba(255, 255, 255, 0.1)",
|
||||
actionBgHover: "rgba(255, 255, 255, 0.2)",
|
||||
actionBorder: "rgba(255, 255, 255, 0.2)",
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
// Main backgrounds
|
||||
modalBackground: "#ffffff",
|
||||
contentBackground: "#f8f9fa",
|
||||
panelBackground: "#ffffff",
|
||||
inputBackground: "#ffffff",
|
||||
|
||||
// Text colors
|
||||
primaryText: "#212529",
|
||||
secondaryText: "#495057",
|
||||
mutedText: "#6c757d",
|
||||
|
||||
// Borders and dividers
|
||||
borderColor: "#dee2e6",
|
||||
dividerColor: "#e9ecef",
|
||||
|
||||
// Interactive elements
|
||||
buttonBackground: "#f8f9fa",
|
||||
buttonHover: "#e9ecef",
|
||||
buttonBorder: "#dee2e6",
|
||||
|
||||
// Message bubbles
|
||||
userMessageBg: "#667eea",
|
||||
userMessageText: "#ffffff",
|
||||
aiMessageBg: "#ffffff",
|
||||
aiMessageText: "#212529",
|
||||
|
||||
// Input focus
|
||||
focusBorder: "#667eea",
|
||||
|
||||
// Shadows
|
||||
shadowColor: "rgba(0,0,0,0.3)",
|
||||
lightShadow: "rgba(0,0,0,0.1)",
|
||||
|
||||
// Action buttons
|
||||
actionBg: "rgba(255, 255, 255, 0.2)",
|
||||
actionBgHover: "rgba(255, 255, 255, 0.3)",
|
||||
actionBorder: "rgba(255, 255, 255, 0.3)",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure font is loaded when module is imported
|
||||
if (typeof document !== "undefined") {
|
||||
initializeChunkFiveFont();
|
||||
|
|
@ -4389,13 +3518,12 @@ export async function openUncloseaiEmbeddedModal() {
|
|||
JSON.stringify(savedHistory),
|
||||
);
|
||||
// Update global chat history
|
||||
chatHistory = [
|
||||
{
|
||||
role: "system",
|
||||
content: getSystemMessage(),
|
||||
},
|
||||
...savedHistory,
|
||||
];
|
||||
chatHistory.length = 0;
|
||||
chatHistory.push({
|
||||
role: "system",
|
||||
content: getSystemMessage(),
|
||||
});
|
||||
chatHistory.push(...savedHistory);
|
||||
restoreConversationHistory();
|
||||
}
|
||||
messageDiv.remove();
|
||||
|
|
@ -4976,7 +4104,7 @@ export function openTTSModal() {
|
|||
const selectedVoice = document.querySelector(
|
||||
'input[name="tts-voice"]:checked',
|
||||
).value;
|
||||
const selectedSpeed = parseFloat(speedSlider.value);
|
||||
const selectedSpeed = Number.parseFloat(speedSlider.value);
|
||||
|
||||
if (!currentText) return;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue