Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Code scanning - action / CodeQL-Build (push) Failing after 2s
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
171 lines
5.7 KiB
TypeScript
171 lines
5.7 KiB
TypeScript
import type { IRoom, ISubscription, ISupportedLanguage, ITranslatedMessage, MessageAttachmentDefault } from '@rocket.chat/core-typings';
|
|
import { isTranslatedMessageAttachment } from '@rocket.chat/core-typings';
|
|
import mem from 'mem';
|
|
|
|
import { PermissionsCachedStore } from '../../cachedStores';
|
|
import { Messages, Subscriptions, Users } from '../../stores';
|
|
import { hasTranslationLanguageInAttachments, hasTranslationLanguageInMessage } from '../../views/room/MessageList/lib/autoTranslate';
|
|
import { sdk } from '../SDKClient';
|
|
import { hasPermission } from '../authorization';
|
|
import { settings } from '../settings';
|
|
import { getUserId, userIdStore } from '../user';
|
|
|
|
let userLanguage = 'en';
|
|
let username = '';
|
|
|
|
const refreshUserCache = () => {
|
|
const uid = userIdStore.getState();
|
|
const user = uid ? Users.use.getState().get(uid) : undefined;
|
|
if (!user) return;
|
|
userLanguage = user.language || 'en';
|
|
username = user.username || '';
|
|
};
|
|
refreshUserCache();
|
|
userIdStore.subscribe(refreshUserCache);
|
|
Users.use.subscribe(refreshUserCache);
|
|
|
|
export const AutoTranslate = {
|
|
initialized: false,
|
|
providersMetadata: {} as { [providerNamer: string]: { name: string; displayName: string } },
|
|
messageIdsToWait: {} as { [messageId: string]: boolean },
|
|
supportedLanguages: [] as ISupportedLanguage[] | undefined,
|
|
|
|
findSubscriptionByRid: mem((rid) => Subscriptions.state.find((record) => record.rid === rid)),
|
|
|
|
getLanguage(rid: IRoom['_id']): string {
|
|
let subscription: ISubscription | undefined;
|
|
if (rid) {
|
|
subscription = this.findSubscriptionByRid(rid);
|
|
}
|
|
const language = (subscription?.autoTranslateLanguage || userLanguage || window.defaultUserLanguage?.()) as string;
|
|
if (language.indexOf('-') !== -1) {
|
|
if (!(this.supportedLanguages || []).some((supportedLanguage) => supportedLanguage.language === language)) {
|
|
return language.slice(0, 2);
|
|
}
|
|
}
|
|
return language;
|
|
},
|
|
|
|
translateAttachments(
|
|
attachments: MessageAttachmentDefault[],
|
|
language: string,
|
|
autoTranslateShowInverse: boolean,
|
|
): MessageAttachmentDefault[] {
|
|
if (!isTranslatedMessageAttachment(attachments)) {
|
|
return attachments;
|
|
}
|
|
for (const attachment of attachments) {
|
|
if (attachment.author_name !== username) {
|
|
if (attachment.text && attachment.translations && attachment.translations[language]) {
|
|
attachment.translations.original = attachment.text;
|
|
|
|
if (autoTranslateShowInverse) {
|
|
attachment.text = attachment.translations.original;
|
|
} else {
|
|
attachment.text = attachment.translations[language];
|
|
}
|
|
}
|
|
|
|
if (attachment.description && attachment.translations && attachment.translations[language]) {
|
|
attachment.translations.original = attachment.description;
|
|
|
|
if (autoTranslateShowInverse) {
|
|
attachment.description = attachment.translations.original;
|
|
} else {
|
|
attachment.description = attachment.translations[language];
|
|
}
|
|
}
|
|
|
|
if (attachment.attachments && attachment.attachments.length > 0) {
|
|
// @ts-expect-error - not sure what to do with this
|
|
attachment.attachments = this.translateAttachments(attachment.attachments, language);
|
|
}
|
|
}
|
|
}
|
|
return attachments;
|
|
},
|
|
|
|
init(): void {
|
|
if (this.initialized) {
|
|
return;
|
|
}
|
|
|
|
const loadProviders = async () => {
|
|
try {
|
|
const [providersMetadata, supportedLanguagesResponse] = await Promise.all([
|
|
sdk.call('autoTranslate.getProviderUiMetadata'),
|
|
sdk.rest.get('/v1/autotranslate.getSupportedLanguages', { targetLanguage: 'en' }),
|
|
]);
|
|
this.providersMetadata = providersMetadata;
|
|
this.supportedLanguages = supportedLanguagesResponse.languages;
|
|
} catch (e: unknown) {
|
|
// Avoid unwanted error message on UI when autotranslate is disabled while fetching data
|
|
console.error((e as Error).message);
|
|
}
|
|
};
|
|
|
|
let loaded = false;
|
|
const unsubs: Array<() => void> = [];
|
|
const tryLoad = async () => {
|
|
if (loaded) return;
|
|
if (!settings.peek('AutoTranslate_Enabled') || !userIdStore.getState() || !hasPermission('auto-translate')) {
|
|
return;
|
|
}
|
|
loaded = true;
|
|
unsubs.splice(0).forEach((unsubscribe) => unsubscribe());
|
|
await loadProviders();
|
|
};
|
|
|
|
unsubs.push(userIdStore.subscribe(() => void tryLoad()));
|
|
unsubs.push(settings.observe('AutoTranslate_Enabled', () => void tryLoad()));
|
|
unsubs.push(PermissionsCachedStore.useReady.subscribe(() => void tryLoad()));
|
|
|
|
void tryLoad();
|
|
|
|
Subscriptions.use.subscribe(() => {
|
|
mem.clear(this.findSubscriptionByRid);
|
|
});
|
|
|
|
this.initialized = true;
|
|
},
|
|
};
|
|
|
|
export const createAutoTranslateMessageStreamHandler = (): ((message: ITranslatedMessage) => void) => {
|
|
AutoTranslate.init();
|
|
|
|
return (message: ITranslatedMessage): void => {
|
|
if (message.u && message.u._id !== getUserId()) {
|
|
const subscription = AutoTranslate.findSubscriptionByRid(message.rid);
|
|
const language = AutoTranslate.getLanguage(message.rid);
|
|
if (
|
|
subscription &&
|
|
subscription.autoTranslate === true &&
|
|
message.msg &&
|
|
(!message.translations ||
|
|
(!hasTranslationLanguageInMessage(message, language) && !hasTranslationLanguageInAttachments(message.attachments, language)))
|
|
) {
|
|
Messages.state.update(
|
|
(record) => record._id === message._id,
|
|
(record) => ({
|
|
...record,
|
|
autoTranslateFetching: true,
|
|
}),
|
|
);
|
|
} else if (AutoTranslate.messageIdsToWait[message._id] !== undefined && subscription && subscription.autoTranslate !== true) {
|
|
Messages.state.update(
|
|
(record) => record._id === message._id,
|
|
({ autoTranslateFetching: _, ...record }) => ({
|
|
...record,
|
|
}),
|
|
);
|
|
delete AutoTranslate.messageIdsToWait[message._id];
|
|
} else if (message.autoTranslateFetching === true) {
|
|
Messages.state.update(
|
|
(record) => record._id === message._id,
|
|
({ autoTranslateFetching: _, ...record }) => record,
|
|
);
|
|
}
|
|
}
|
|
};
|
|
};
|