mirror of
https://github.com/zclzone/vue-naive-admin.git
synced 2026-06-21 13:54:07 +08:00
123 lines
3.3 KiB
JavaScript
123 lines
3.3 KiB
JavaScript
export const STORE_KEY = 'demo-translate-settings'
|
|
|
|
export const API_BASE_URL_OPTIONS = [
|
|
'https://runapi.co/v1',
|
|
]
|
|
|
|
export const MODEL_OPTIONS = [
|
|
'gpt-5.4-mini',
|
|
'gpt-5.2',
|
|
'gpt-5.4',
|
|
'gpt-5.5',
|
|
'deepseek-v4-flash',
|
|
'deepseek-v4-pro',
|
|
]
|
|
|
|
export const DEFAULT_SETTINGS = {
|
|
apiKey: 'sk-17UVTOjYEB3M9Erg32yGdEhMVFKEaALuIkjy1CkGrCUdiQqJ',
|
|
apiBaseUrl: API_BASE_URL_OPTIONS[0],
|
|
model: MODEL_OPTIONS[0],
|
|
}
|
|
|
|
export const EXPERIENCE_KEY_MAX_TEXT_LENGTH = 100
|
|
|
|
const TRANSLATION_RULES = 'Translate directly. Return only the translation. Preserve formatting, markdown, code, URLs, numbers, placeholders, and line breaks.'
|
|
|
|
export function isSettingsReady(target) {
|
|
return Boolean(target.apiKey?.trim() && target.apiBaseUrl?.trim() && target.model?.trim())
|
|
}
|
|
|
|
export function isExperienceApiKey(apiKey) {
|
|
return apiKey?.trim() === DEFAULT_SETTINGS.apiKey
|
|
}
|
|
|
|
export function validateTranslateText(text, settings) {
|
|
if (isExperienceApiKey(settings.apiKey) && text.length > EXPERIENCE_KEY_MAX_TEXT_LENGTH)
|
|
throw new Error(`体验 Key 最多支持 ${EXPERIENCE_KEY_MAX_TEXT_LENGTH} 字符`)
|
|
}
|
|
|
|
function countMatches(text, pattern) {
|
|
return text.match(pattern)?.length || 0
|
|
}
|
|
|
|
function detectSourceLanguage(text) {
|
|
const cjkCount = countMatches(text, /[\u3400-\u9FFF]/g)
|
|
const latinCount = countMatches(text, /[A-Z]/gi)
|
|
|
|
if (!cjkCount && !latinCount)
|
|
return 'auto'
|
|
if (!cjkCount)
|
|
return 'en'
|
|
if (!latinCount)
|
|
return 'zh'
|
|
|
|
const cjkRatio = cjkCount / (cjkCount + latinCount)
|
|
if (cjkRatio >= 0.2)
|
|
return 'zh'
|
|
if (cjkRatio <= 0.08)
|
|
return 'en'
|
|
|
|
const firstScript = text.match(/[\u3400-\u9FFFA-Z]/i)?.[0]
|
|
return firstScript && /[\u3400-\u9FFF]/.test(firstScript) ? 'zh' : 'en'
|
|
}
|
|
|
|
function createSystemPrompt(sourceLanguage) {
|
|
if (sourceLanguage === 'zh')
|
|
return `${TRANSLATION_RULES} Target: natural English.`
|
|
if (sourceLanguage === 'en')
|
|
return `${TRANSLATION_RULES} Target: natural Simplified Chinese.`
|
|
return `${TRANSLATION_RULES} Target: Simplified Chinese.`
|
|
}
|
|
|
|
function createFastModelOptions(model) {
|
|
const normalized = model.trim().toLowerCase()
|
|
if (!normalized.startsWith('gpt-5'))
|
|
return {}
|
|
|
|
return {
|
|
reasoning_effort: /^gpt-5\.[1-9]\d*/.test(normalized) ? 'none' : 'low',
|
|
verbosity: 'low',
|
|
}
|
|
}
|
|
|
|
export async function translateText(text, settings) {
|
|
validateTranslateText(text, settings)
|
|
|
|
const baseUrl = settings.apiBaseUrl.trim().replace(/\/+$/, '')
|
|
const model = settings.model.trim() || DEFAULT_SETTINGS.model
|
|
const sourceLanguage = detectSourceLanguage(text)
|
|
|
|
const response = await fetch(`${baseUrl}/chat/completions`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': `Bearer ${settings.apiKey.trim()}`,
|
|
},
|
|
body: JSON.stringify({
|
|
model,
|
|
temperature: 0,
|
|
...createFastModelOptions(model),
|
|
messages: [
|
|
{
|
|
role: 'system',
|
|
content: createSystemPrompt(sourceLanguage),
|
|
},
|
|
{
|
|
role: 'user',
|
|
content: text,
|
|
},
|
|
],
|
|
}),
|
|
})
|
|
|
|
const data = await response.json().catch(() => ({}))
|
|
if (!response.ok)
|
|
throw new Error(data?.error?.message || `请求失败:${response.status}`)
|
|
|
|
const result = data?.choices?.[0]?.message?.content?.trim() || ''
|
|
if (!result)
|
|
throw new Error('未收到翻译结果')
|
|
|
|
return result
|
|
}
|