// Minimal shims for the experimental Chrome AI APIs used here. // https://developer.chrome.com/docs/ai/language-detection // https://developer.chrome.com/docs/ai/translator-api type AIAvailability = | 'unavailable' | 'downloadable' | 'downloading' | 'available'; interface AIMonitorEvent { loaded: number; } interface AIMonitor { addEventListener( type: 'downloadprogress', listener: (event: AIMonitorEvent) => void, ): void; } interface LanguageDetectorInstance { ready: Promise; detect( text: string, ): Promise>; } interface LanguageDetectorStatic { availability(): Promise; create(options?: { monitor?: (m: AIMonitor) => void; }): Promise; } interface TranslatorInstance { ready: Promise; translate(text: string): Promise; } interface TranslatorStatic { availability(options: { sourceLanguage: string; targetLanguage: string; }): Promise; create(options: { sourceLanguage: string; targetLanguage: string; monitor?: (m: AIMonitor) => void; }): Promise; } declare const LanguageDetector: LanguageDetectorStatic; declare const Translator: TranslatorStatic; const supportsLanguageDetector = 'LanguageDetector' in self; export const supportsBrowserTranslator = supportsLanguageDetector && 'Translator' in self; // https://developer.chrome.com/docs/ai/language-detection export let langDetector: LanguageDetectorInstance | undefined; if (supportsLanguageDetector) { void (async () => { try { const availability = await LanguageDetector.availability(); if (availability === 'unavailable') { console.log('🎺 Language detector is unavailable'); // The language detector isn't usable. return; } if (availability === 'available') { console.log('🎺 Language detector is available'); // The language detector can immediately be used. langDetector = await LanguageDetector.create(); } else { console.log(`🎺 Language detector is ${availability}`); // The language detector can be used after model download. langDetector = await LanguageDetector.create({ monitor(m) { m.addEventListener('downloadprogress', (e) => { console.log( `🎺 Language detector: Downloaded ${e.loaded * 100}%`, ); }); }, }); await langDetector.ready; } } catch (e) { console.error(e); } })(); } export interface TranslateResult { content?: string; detectedSourceLanguage?: string; provider?: string; error?: unknown; } // console.groupEnd() takes no arguments per the spec; the original JS passed a // label as a harmless no-op. The call-site cast preserves both runtime call // shape (method on `console`) and the original argument. type ConsoleGroupEnd = (label?: string) => void; // https://developer.chrome.com/docs/ai/translator-api export const translate = async ( text: string, source: string, target: string, ): Promise => { let detectedSourceLanguage: string | undefined; const originalSource = source; if (source === 'auto') { if (!langDetector?.detect) { return { error: 'No language detector', }; } try { const results = await langDetector.detect(text); source = results[0].detectedLanguage; detectedSourceLanguage = source; } catch (e) { console.warn(e); return { error: e, }; } } const groupLabel = `💬 BROWSER TRANSLATE ${text}`; console.groupCollapsed(groupLabel); console.log(originalSource, detectedSourceLanguage, target); try { const translatorCapabilities = await Translator.availability({ sourceLanguage: source, targetLanguage: target, }); // Note: Translator.availability() returns 'unavailable', 'downloadable', 'downloading', or 'available'. if (translatorCapabilities === 'unavailable') { (console.groupEnd as ConsoleGroupEnd)(groupLabel); return { error: `Unsupported language pair: ${source} -> ${target}`, }; } let translator: TranslatorInstance; if (translatorCapabilities === 'available') { translator = await Translator.create({ sourceLanguage: source, targetLanguage: target, }); } else { translator = await Translator.create({ sourceLanguage: source, targetLanguage: target, monitor(m) { m.addEventListener('downloadprogress', (e) => { console.log( `Translate ${source} -> ${target}: Downloaded ${e.loaded * 100}%`, ); }); }, }); await translator.ready; } const content = await translator.translate(text); console.log(content); (console.groupEnd as ConsoleGroupEnd)(groupLabel); return { content, detectedSourceLanguage, provider: 'browser', }; } catch (e) { (console.groupEnd as ConsoleGroupEnd)(groupLabel); console.error(e); return { error: e, }; } };