import type { EventTypeTranslationRepository } from "@calcom/features/eventTypeTranslation/repositories/EventTypeTranslationRepository"; import { locales as i18nLocales } from "@calcom/lib/i18n"; import logger from "@calcom/lib/logger"; import { TRANSLATION_SUPPORTED_LOCALES, type TranslationSupportedLocale, } from "@calcom/lib/translationConstants"; import { EventTypeAutoTranslatedField } from "@calcom/prisma/enums"; import type { EventTypeTranslationLookupOptions, EventTypeTranslationLookupResult, ITranslationService, TranslateTextParams, TranslateTextResult, TranslationResult, } from "./ITranslationService"; export interface ITranslationServiceDeps { localizeText: (text: string, sourceLocale: string, targetLocale: string) => Promise; eventTypeTranslationRepository: EventTypeTranslationRepository; } export class TranslationService implements ITranslationService { constructor(private deps: ITranslationServiceDeps) {} getTargetLocales(sourceLocale: string): TranslationSupportedLocale[] { return TRANSLATION_SUPPORTED_LOCALES.filter( (locale) => locale !== sourceLocale && i18nLocales.includes(locale) ); } async translateText(params: TranslateTextParams): Promise { const { text, sourceLocale } = params; if (!text?.trim()) { return { translations: [], failedLocales: [] }; } const targetLocales = this.getTargetLocales(sourceLocale); const failedLocales: string[] = []; try { const translationPromises = targetLocales.map(async (targetLocale) => { const translatedText = await this.deps.localizeText(text, sourceLocale, targetLocale); return { translatedText, targetLocale }; }); const results = await Promise.all(translationPromises); const translations: TranslationResult[] = []; for (const result of results) { if (result.translatedText !== null) { translations.push({ translatedText: result.translatedText, targetLocale: result.targetLocale, }); } else { failedLocales.push(result.targetLocale); } } return { translations, failedLocales }; } catch (error) { logger.error("TranslationService.translateText() failed:", error); return { translations: [], failedLocales: targetLocales }; } } async getEventTypeTranslation( eventTypeId: number, targetLocale: string, options: EventTypeTranslationLookupOptions = { includeTitle: false, includeDescription: true } ): Promise { const result: EventTypeTranslationLookupResult = {}; const promises: Promise[] = []; if (options.includeTitle) { promises.push( this.deps.eventTypeTranslationRepository .findByLocale(eventTypeId, EventTypeAutoTranslatedField.TITLE, targetLocale) .then((translation) => { if (translation?.translatedText) { result.translatedTitle = translation.translatedText; } }) ); } if (options.includeDescription) { promises.push( this.deps.eventTypeTranslationRepository .findByLocale(eventTypeId, EventTypeAutoTranslatedField.DESCRIPTION, targetLocale) .then((translation) => { if (translation?.translatedText) { result.translatedDescription = translation.translatedText; } }) ); } await Promise.all(promises); return result; } }