...
This commit is contained in:
@@ -1,2 +1,2 @@
|
||||
export * from './translator-action';
|
||||
export * from './translator-action-dto';
|
||||
export * from './translator-action-dto';
|
||||
|
||||
@@ -1,40 +1,27 @@
|
||||
import { TSharedTranslationResult } from "@/shared";
|
||||
import {
|
||||
LENGTH_MAX_LANGUAGE,
|
||||
LENGTH_MIN_LANGUAGE,
|
||||
LENGTH_MAX_TRANSLATOR_TEXT,
|
||||
LENGTH_MIN_TRANSLATOR_TEXT,
|
||||
} from "@/shared/constant";
|
||||
import { generateValidator } from "@/utils/validate";
|
||||
import z from "zod";
|
||||
|
||||
export interface CreateTranslationHistoryInput {
|
||||
userId?: string;
|
||||
sourceText: string;
|
||||
sourceLanguage: string;
|
||||
targetLanguage: string;
|
||||
translatedText: string;
|
||||
sourceIpa?: string;
|
||||
targetIpa?: string;
|
||||
}
|
||||
const schemaActionInputTranslateText = z.object({
|
||||
sourceText: z.string().min(LENGTH_MIN_TRANSLATOR_TEXT).max(LENGTH_MAX_TRANSLATOR_TEXT),
|
||||
targetLanguage: z.string().min(LENGTH_MIN_LANGUAGE).max(LENGTH_MAX_LANGUAGE),
|
||||
forceRetranslate: z.boolean().optional().default(false),
|
||||
needIpa: z.boolean().optional().default(true),
|
||||
userId: z.string().optional(),
|
||||
});
|
||||
|
||||
export interface TranslationHistoryQuery {
|
||||
sourceText: string;
|
||||
targetLanguage: string;
|
||||
}
|
||||
export type ActionInputTranslateText = z.infer<typeof schemaActionInputTranslateText>;
|
||||
|
||||
export interface TranslateTextInput {
|
||||
sourceText: string;
|
||||
targetLanguage: string;
|
||||
forceRetranslate?: boolean; // 默认 false
|
||||
needIpa?: boolean; // 默认 true
|
||||
userId?: string; // 可选用户 ID
|
||||
}
|
||||
export const validateActionInputTranslateText = generateValidator(schemaActionInputTranslateText);
|
||||
|
||||
export interface TranslateTextOutput {
|
||||
sourceText: string;
|
||||
translatedText: string;
|
||||
sourceLanguage: string;
|
||||
targetLanguage: string;
|
||||
sourceIpa: string; // 如果 needIpa=false,返回空字符串
|
||||
targetIpa: string; // 如果 needIpa=false,返回空字符串
|
||||
}
|
||||
|
||||
export interface TranslationLLMResponse {
|
||||
translatedText: string;
|
||||
sourceLanguage: string;
|
||||
targetLanguage: string;
|
||||
sourceIpa?: string; // 可选,根据 needIpa 决定
|
||||
targetIpa?: string; // 可选,根据 needIpa 决定
|
||||
}
|
||||
export type ActionOutputTranslateText = {
|
||||
message: string;
|
||||
success: boolean;
|
||||
data?: TSharedTranslationResult;
|
||||
};
|
||||
|
||||
@@ -1 +1,103 @@
|
||||
"use server";
|
||||
|
||||
import {
|
||||
ActionInputTranslateText,
|
||||
ActionOutputTranslateText,
|
||||
validateActionInputTranslateText,
|
||||
} from "./translator-action-dto";
|
||||
import { ValidateError } from "@/lib/errors";
|
||||
import { serviceTranslateText } from "./translator-service";
|
||||
import { getAnswer } from "@/lib/bigmodel/zhipu";
|
||||
|
||||
export const actionTranslateText = async (
|
||||
dto: ActionInputTranslateText
|
||||
): Promise<ActionOutputTranslateText> => {
|
||||
try {
|
||||
return {
|
||||
message: "success",
|
||||
success: true,
|
||||
data: await serviceTranslateText(validateActionInputTranslateText(dto)),
|
||||
};
|
||||
} catch (e) {
|
||||
if (e instanceof ValidateError) {
|
||||
return {
|
||||
success: false,
|
||||
message: e.message,
|
||||
};
|
||||
}
|
||||
console.log(e);
|
||||
return {
|
||||
success: false,
|
||||
message: "Unknown error occurred.",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @deprecated 保留此函数以支持旧代码(text-speaker 功能)
|
||||
*/
|
||||
export const genIPA = async (text: string) => {
|
||||
return (
|
||||
"[" +
|
||||
(
|
||||
await getAnswer(
|
||||
`
|
||||
<text>${text}</text>
|
||||
|
||||
请生成以上文本的严式国际音标
|
||||
然后直接发给我
|
||||
不要附带任何说明
|
||||
不要擅自增减符号
|
||||
不许用"/"或者"[]"包裹
|
||||
`.trim(),
|
||||
)
|
||||
)
|
||||
.replaceAll("[", "")
|
||||
.replaceAll("]", "") +
|
||||
"]"
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @deprecated 保留此函数以支持旧代码(text-speaker 功能)
|
||||
*/
|
||||
export const genLanguage = async (text: string) => {
|
||||
const language = await getAnswer([
|
||||
{
|
||||
role: "system",
|
||||
content: `
|
||||
你是一个语言检测工具。请识别文本的语言并返回语言名称。
|
||||
|
||||
返回语言的标准英文名称,例如:
|
||||
- 中文: Chinese
|
||||
- 英语: English
|
||||
- 日语: Japanese
|
||||
- 韩语: Korean
|
||||
- 法语: French
|
||||
- 德语: German
|
||||
- 意大利语: Italian
|
||||
- 葡萄牙语: Portuguese
|
||||
- 西班牙语: Spanish
|
||||
- 俄语: Russian
|
||||
- 阿拉伯语: Arabic
|
||||
- 印地语: Hindi
|
||||
- 泰语: Thai
|
||||
- 越南语: Vietnamese
|
||||
- 等等...
|
||||
|
||||
如果无法识别语言,返回 "Unknown"
|
||||
|
||||
规则:
|
||||
1. 只返回语言的标准英文名称
|
||||
2. 首字母大写,其余小写
|
||||
3. 不要附带任何说明
|
||||
4. 不要擅自增减符号
|
||||
`.trim()
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
content: `<text>${text}</text>`
|
||||
}
|
||||
]);
|
||||
return language.trim();
|
||||
};
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
export type RepoInputSelectLatestTranslation = {
|
||||
sourceText: string;
|
||||
targetLanguage: string;
|
||||
};
|
||||
|
||||
export type RepoOutputSelectLatestTranslation = {
|
||||
id: number;
|
||||
translatedText: string;
|
||||
sourceLanguage: string;
|
||||
targetLanguage: string;
|
||||
sourceIpa: string | null;
|
||||
targetIpa: string | null;
|
||||
} | null;
|
||||
|
||||
export type RepoInputCreateTranslationHistory = {
|
||||
userId?: string;
|
||||
sourceText: string;
|
||||
sourceLanguage: string;
|
||||
targetLanguage: string;
|
||||
translatedText: string;
|
||||
sourceIpa?: string;
|
||||
targetIpa?: string;
|
||||
};
|
||||
|
||||
@@ -1,31 +1,41 @@
|
||||
"use server";
|
||||
|
||||
import { CreateTranslationHistoryInput, TranslationHistoryQuery } from "./translator-action-dto";
|
||||
import {
|
||||
RepoInputCreateTranslationHistory,
|
||||
RepoInputSelectLatestTranslation,
|
||||
RepoOutputSelectLatestTranslation,
|
||||
} from "./translator-repository-dto";
|
||||
import prisma from "@/lib/db";
|
||||
|
||||
/**
|
||||
* 创建翻译历史记录
|
||||
*/
|
||||
export async function repoCreateTranslationHistory(data: CreateTranslationHistoryInput) {
|
||||
return prisma.translationHistory.create({
|
||||
data: data,
|
||||
});
|
||||
export async function repoSelectLatestTranslation(
|
||||
dto: RepoInputSelectLatestTranslation
|
||||
): Promise<RepoOutputSelectLatestTranslation> {
|
||||
const result = await prisma.translationHistory.findFirst({
|
||||
where: {
|
||||
sourceText: dto.sourceText,
|
||||
targetLanguage: dto.targetLanguage,
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: "desc",
|
||||
},
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
id: result.id,
|
||||
translatedText: result.translatedText,
|
||||
sourceLanguage: result.sourceLanguage,
|
||||
targetLanguage: result.targetLanguage,
|
||||
sourceIpa: result.sourceIpa,
|
||||
targetIpa: result.targetIpa,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询最新的翻译记录
|
||||
* @param sourceText 源文本
|
||||
* @param targetLanguage 目标语言
|
||||
* @returns 最新的翻译记录,如果不存在则返回 null
|
||||
*/
|
||||
export async function repoSelectLatestTranslation(query: TranslationHistoryQuery) {
|
||||
return prisma.translationHistory.findFirst({
|
||||
where: {
|
||||
sourceText: query.sourceText,
|
||||
targetLanguage: query.targetLanguage,
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc',
|
||||
},
|
||||
});
|
||||
export async function repoCreateTranslationHistory(
|
||||
data: RepoInputCreateTranslationHistory
|
||||
) {
|
||||
return await prisma.translationHistory.create({
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { TSharedTranslationResult } from "@/shared";
|
||||
|
||||
export type ServiceInputTranslateText = {
|
||||
sourceText: string;
|
||||
targetLanguage: string;
|
||||
forceRetranslate: boolean;
|
||||
needIpa: boolean;
|
||||
userId?: string;
|
||||
};
|
||||
|
||||
export type ServiceOutputTranslateText = TSharedTranslationResult;
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import { executeTranslation } from "@/lib/bigmodel/translator";
|
||||
import { repoCreateTranslationHistory, repoSelectLatestTranslation } from "./translator-repository";
|
||||
import { ServiceInputTranslateText, ServiceOutputTranslateText } from "./translator-service-dto";
|
||||
|
||||
export const serviceTranslateText = async (
|
||||
dto: ServiceInputTranslateText
|
||||
): Promise<ServiceOutputTranslateText> => {
|
||||
const { sourceText, targetLanguage, forceRetranslate, needIpa, userId } = dto;
|
||||
|
||||
// Check for existing translation
|
||||
const lastTranslation = await repoSelectLatestTranslation({
|
||||
sourceText,
|
||||
targetLanguage,
|
||||
});
|
||||
|
||||
if (forceRetranslate || !lastTranslation) {
|
||||
// Call AI for translation
|
||||
const response = await executeTranslation(
|
||||
sourceText,
|
||||
targetLanguage,
|
||||
needIpa
|
||||
);
|
||||
|
||||
// Save translation history asynchronously (don't block response)
|
||||
repoCreateTranslationHistory({
|
||||
userId,
|
||||
sourceText,
|
||||
sourceLanguage: response.sourceLanguage,
|
||||
targetLanguage: response.targetLanguage,
|
||||
translatedText: response.translatedText,
|
||||
sourceIpa: needIpa ? response.sourceIpa : undefined,
|
||||
targetIpa: needIpa ? response.targetIpa : undefined,
|
||||
}).catch((error) => {
|
||||
console.error("Failed to save translation data:", error);
|
||||
});
|
||||
|
||||
return {
|
||||
sourceText: response.sourceText,
|
||||
translatedText: response.translatedText,
|
||||
sourceLanguage: response.sourceLanguage,
|
||||
targetLanguage: response.targetLanguage,
|
||||
sourceIpa: response.sourceIpa || "",
|
||||
targetIpa: response.targetIpa || "",
|
||||
};
|
||||
} else {
|
||||
// Return cached translation
|
||||
// Still save a history record for analytics
|
||||
repoCreateTranslationHistory({
|
||||
userId,
|
||||
sourceText,
|
||||
sourceLanguage: lastTranslation.sourceLanguage,
|
||||
targetLanguage: lastTranslation.targetLanguage,
|
||||
translatedText: lastTranslation.translatedText,
|
||||
sourceIpa: lastTranslation.sourceIpa || undefined,
|
||||
targetIpa: lastTranslation.targetIpa || undefined,
|
||||
}).catch((error) => {
|
||||
console.error("Failed to save translation data:", error);
|
||||
});
|
||||
|
||||
return {
|
||||
sourceText,
|
||||
translatedText: lastTranslation.translatedText,
|
||||
sourceLanguage: lastTranslation.sourceLanguage,
|
||||
targetLanguage: lastTranslation.targetLanguage,
|
||||
sourceIpa: lastTranslation.sourceIpa || "",
|
||||
targetIpa: lastTranslation.targetIpa || "",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user