-
Notifications
You must be signed in to change notification settings - Fork 0
/
Perplexity-AI-Translator.user.js
175 lines (151 loc) · 6.16 KB
/
Perplexity-AI-Translator.user.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
// ==UserScript==
// @name Perplexity AI Translator
// @namespace http://tampermonkey.net/
// @version 1.4
// @description Translate selected text using Perplexity AI API
// @match *://*/*
// @grant GM_xmlhttpRequest
// @grant GM_addStyle
// @updateURL https://raw.githubusercontent.com/LanGsA0/Perplexity-AI-Translator/main/Perplexity-AI-Translator.user.js
// @downloadURL https://raw.githubusercontent.com/LanGsA0/Perplexity-AI-Translator/main/Perplexity-AI-Translator.user.js
// ==/UserScript==
(function() {
'use strict';
const API_KEY = 'YOUR_PERPLEXITY_API_KEY_HERE'; // 여기에 API 키를 입력하세요
const API_URL = 'https://api.perplexity.ai/chat/completions';
GM_addStyle(`
#perplexity-translator {
background: white;
border: 1px solid #ccc;
padding: 10px;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
z-index: 9999;
max-width: 300px;
display: none;
position: absolute;
font-size: 14px;
line-height: 1.4;
user-select: text;
white-space: pre-wrap;
}
#perplexity-translate-btn {
position: absolute;
background: #4CAF50;
color: white;
border: none;
padding: 5px 10px;
border-radius: 3px;
cursor: pointer;
font-size: 12px;
z-index: 10000;
}
`);
let translatorDiv = document.createElement('div');
translatorDiv.id = 'perplexity-translator';
document.body.appendChild(translatorDiv);
let translateBtn = document.createElement('button');
translateBtn.id = 'perplexity-translate-btn';
translateBtn.textContent = 'Perplexity로 번역';
translateBtn.style.display = 'none';
document.body.appendChild(translateBtn);
let isTranslating = false;
function translateText(text) {
const messages = [
{role: "system", content: "You are a translator. Translate the given text to Korean. Provide only the direct translation without any additional explanations, context, or original text in parentheses. Preserve the original line breaks. Ensure all characters are properly encoded in UTF-8. Do not use any non-Korean characters in your translation."},
{role: "user", content: `Translate the following text to Korean: "${text}"`}
];
GM_xmlhttpRequest({
method: "POST",
url: API_URL,
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${API_KEY}`
},
data: JSON.stringify({
model: "llama-3.1-sonar-large-128k-online",
messages: messages
}),
onload: function(response) {
const result = JSON.parse(response.responseText);
let translation = result.choices[0].message.content.trim();
translation = removeParentheses(translation);
translation = adjustTranslationFormat(translation);
translation = fixEncoding(translation);
showTranslation(translation);
},
onerror: function(error) {
console.error('Translation error:', error);
showTranslation('번역 중 오류가 발생했습니다.');
}
});
}
function removeParentheses(text) {
return text.split('(')[0].trim();
}
function adjustTranslationFormat(translationText) {
const lines = translationText.split('\n');
const formattedTranslation = lines.map(line => {
const sentences = line.split('. ');
return sentences.join('. ');
});
return formattedTranslation.join('\n');
}
function fixEncoding(text) {
// 한글 및 일반적인 문장 부호만 허용
return text.replace(/[^\uAC00-\uD7A3\s.,!?]/g, '');
}
function showTranslation(text) {
translatorDiv.textContent = text;
translatorDiv.style.display = 'block';
const selection = window.getSelection();
const range = selection.getRangeAt(0);
const rect = range.getBoundingClientRect();
translatorDiv.style.top = `${window.scrollY + rect.bottom + 30}px`;
translatorDiv.style.left = `${window.scrollX + rect.left}px`;
translateBtn.style.display = 'none';
isTranslating = true;
}
function isKorean(text) {
// 한글 문자가 포함되어 있는지 확인
return /[\uAC00-\uD7A3]/.test(text);
}
function showTranslateButton(e) {
if (isTranslating) return;
const selection = window.getSelection();
const selectedText = selection.toString().trim();
if (selectedText && !translatorDiv.contains(e.target) && !isKorean(selectedText)) {
const range = selection.getRangeAt(0);
const rect = range.getBoundingClientRect();
translateBtn.style.top = `${window.scrollY + rect.bottom + 5}px`;
translateBtn.style.left = `${window.scrollX + rect.left}px`;
translateBtn.style.display = 'block';
translatorDiv.style.display = 'none';
} else {
translateBtn.style.display = 'none';
}
}
document.addEventListener('mouseup', showTranslateButton);
translateBtn.addEventListener('click', function() {
const selectedText = window.getSelection().toString().trim();
if (selectedText) {
translateText(selectedText);
}
});
document.addEventListener('mousedown', function(e) {
if (e.target !== translateBtn && !translatorDiv.contains(e.target)) {
translateBtn.style.display = 'none';
translatorDiv.style.display = 'none';
isTranslating = false;
}
});
translatorDiv.addEventListener('mousedown', function(e) {
e.stopPropagation();
});
translatorDiv.addEventListener('mouseup', function(e) {
e.stopPropagation();
});
translatorDiv.addEventListener('click', function(e) {
e.stopPropagation();
});
})();