消息
This commit is contained in:
203
TUIKit/components/TUIChat/utils/translation.ts
Normal file
203
TUIKit/components/TUIChat/utils/translation.ts
Normal file
@@ -0,0 +1,203 @@
|
||||
import TUIChatEngine, {
|
||||
IMessageModel,
|
||||
TUIChatService,
|
||||
TUIStore,
|
||||
TUITranslateService,
|
||||
TUIUserService,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import { IChatResponese, IUserProfile } from '../../../interface';
|
||||
|
||||
/**
|
||||
* three type for origin text to be translated
|
||||
* 1. image: small face text
|
||||
* 2. text: plain text
|
||||
* 3. mention: mention '@'
|
||||
*/
|
||||
interface ITextFace {
|
||||
type: 'face';
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface ITextPlain {
|
||||
type: 'text';
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface ITextAt {
|
||||
type: 'mention';
|
||||
value: string;
|
||||
}
|
||||
|
||||
export type TranslationTextType = ITextFace | ITextPlain | ITextAt;
|
||||
|
||||
class Translator {
|
||||
public isUseCache = true;
|
||||
private translationCache = new Map<string, TranslationTextType[]>();
|
||||
private static instance: Translator | undefined = undefined;
|
||||
|
||||
private constructor() {}
|
||||
|
||||
static getInstance() {
|
||||
if (!Translator.instance) {
|
||||
Translator.instance = new Translator();
|
||||
}
|
||||
return Translator.instance;
|
||||
}
|
||||
|
||||
async get(message: IMessageModel): Promise<TranslationTextType[]> {
|
||||
// step1: check in cache if translation exist
|
||||
if (this.isUseCache) {
|
||||
const cache = this.translationCache.get(message.ID);
|
||||
if (cache !== undefined) {
|
||||
return cache;
|
||||
}
|
||||
}
|
||||
|
||||
// step2: get message model with prototype methods
|
||||
const currentMessage: IMessageModel = TUIStore.getMessageModel(message.ID);
|
||||
if (!currentMessage) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const { text } = currentMessage.getMessageContent() || {};
|
||||
const textList: TranslationTextType[] = [];
|
||||
const splittingList = await this.getNickList(currentMessage);
|
||||
// step3: Categorize origin messages to 'plain text', 'face', 'mention'
|
||||
for (let i = 0; i < text.length; ++i) {
|
||||
const item = text[i];
|
||||
if (item.name === 'img') {
|
||||
textList.push({ type: 'face', value: item.src });
|
||||
continue;
|
||||
}
|
||||
const { transSplitingList, atNickList } = this.getSplitResult(item.text, splittingList);
|
||||
for (let j = 0; j < transSplitingList.length; ++j) {
|
||||
textList.push({ type: 'text', value: transSplitingList[j] });
|
||||
if (j < atNickList.length) {
|
||||
textList.push({ type: 'mention', value: atNickList[j] });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// step4: filter plain text to be translated
|
||||
const needTranslateTextIndex: number[] = [];
|
||||
const needTranslateText = textList.filter((item, index) => {
|
||||
if (item.type === 'text' && item.value.trim() !== '') {
|
||||
needTranslateTextIndex.push(index);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}).map(item => item.value);
|
||||
|
||||
if (needTranslateText.length === 0) {
|
||||
this.translationCache.set(currentMessage.ID, textList);
|
||||
return textList;
|
||||
}
|
||||
// step5: get final translation result
|
||||
const translationResult = await this.getTranslationStandard(needTranslateText) as string[];
|
||||
translationResult.forEach((item, index) => {
|
||||
textList[needTranslateTextIndex[index]].value = item;
|
||||
});
|
||||
|
||||
// step6: cache translation result
|
||||
this.translationCache.set(currentMessage.ID, textList);
|
||||
return textList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the translation cache.
|
||||
*/
|
||||
clear() {
|
||||
this.translationCache.clear();
|
||||
}
|
||||
|
||||
disableCache() {
|
||||
this.isUseCache = false;
|
||||
}
|
||||
|
||||
enableCache() {
|
||||
this.isUseCache = true;
|
||||
}
|
||||
|
||||
private getTranslationStandard(originTextList: string[]): Promise<string[]> {
|
||||
return new Promise((resolve, reject) => {
|
||||
TUIChatService.translateText({
|
||||
sourceTextList: originTextList,
|
||||
sourceLanguage: 'auto',
|
||||
})
|
||||
.then((response: IChatResponese<{ translatedTextList: string[] }>) => {
|
||||
const {
|
||||
data: { translatedTextList },
|
||||
} = response;
|
||||
resolve(translatedTextList);
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* the nick list is used to split the text by @ + {nick or userID}
|
||||
* @param message
|
||||
* @returns e.g. ['@james', '@john']
|
||||
*/
|
||||
private async getNickList(message: IMessageModel): Promise<string[]> {
|
||||
const splittingList: string[] = [];
|
||||
const { atUserList = [] } = message;
|
||||
const atAllID: string = TUIChatEngine.TYPES.MSG_AT_ALL;
|
||||
if (atUserList.includes(atAllID)) {
|
||||
splittingList.push(`@${TUITranslateService.t('TUIChat.所有人')}`);
|
||||
}
|
||||
if (atUserList.length > 0) {
|
||||
const { data: userProfileList } = await TUIUserService.getUserProfile({ userIDList: atUserList }) as IChatResponese<IUserProfile[]>;
|
||||
userProfileList.forEach((user) => {
|
||||
const atNick = `@${user.nick || user.userID}`;
|
||||
splittingList.push(atNick);
|
||||
});
|
||||
}
|
||||
return [...new Set(splittingList)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits the given text into substrings based on the provided splitString array.
|
||||
*
|
||||
* @param {string} text - The text to be split.
|
||||
* @param {string[]} splitString - The array of strings to split the text by.
|
||||
* @return {{ transSplitingList: string[]; atNickList: string[] }} - An object containing two arrays:
|
||||
* - transSplitingList: An array of substrings extracted from the text.
|
||||
* - atNickList: An array of split strings that were found in the text.
|
||||
*/
|
||||
private getSplitResult(text: string, splitString: string[]): { transSplitingList: string[]; atNickList: string[] } {
|
||||
let searchStartPos = 0;
|
||||
const transSplitingList: string[] = [];
|
||||
const atNickList: string[] = [];
|
||||
while (searchStartPos < text.length) {
|
||||
const nextAtCharPos = text.indexOf('@', searchStartPos);
|
||||
if (nextAtCharPos === -1) {
|
||||
transSplitingList.push(text.substring(searchStartPos));
|
||||
break;
|
||||
}
|
||||
let found = false;
|
||||
for (let i = 0; i < splitString.length; ++i) {
|
||||
const pos = text.indexOf(splitString[i], nextAtCharPos);
|
||||
if (pos !== -1 && pos === nextAtCharPos) {
|
||||
transSplitingList.push(text.substring(searchStartPos, pos));
|
||||
atNickList.push(splitString[i]);
|
||||
searchStartPos = pos + splitString[i].length;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
transSplitingList.push(text.substring(searchStartPos));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return {
|
||||
transSplitingList,
|
||||
atNickList,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const translator = Translator.getInstance();
|
||||
Reference in New Issue
Block a user