Skip to content

Instantly share code, notes, and snippets.

@flipeador
Last active January 30, 2026 05:56
Show Gist options
  • Select an option

  • Save flipeador/56ca9b5798987d175f0a5e9bc97f6977 to your computer and use it in GitHub Desktop.

Select an option

Save flipeador/56ca9b5798987d175f0a5e9bc97f6977 to your computer and use it in GitHub Desktop.
Translate Bluesky posts using the Translator API.
// ==UserScript==
// @name Bluesky Translate
// @author Flipeador
// @version 1.0.3
// @icon https://www.google.com/s2/favicons?sz=128&domain=bsky.app
// @homepageURL https://gist.github.com/flipeador/56ca9b5798987d175f0a5e9bc97f6977
// @downloadURL https://gist.github.com/flipeador/56ca9b5798987d175f0a5e9bc97f6977/raw/bluesky-translate.js
// @match https://bsky.app/*
// @run-at document-idle
// ==/UserScript==
// https://developer.chrome.com/docs/ai/built-in
const gtUrl = 'https://translate.google.com/';
const selector = `a[href^="${gtUrl}"]:not([_])`;
async function main() {
for (const $a of document.querySelectorAll(selector)) {
$a.setAttribute('_', '');
$a.setAttribute('title', 'Detecting language…');
const text = new URL($a.href).searchParams.get('text');
const detectedLanguage = await detectLanguage(text);
if (!detectedLanguage || Error.isError(detectedLanguage)) {
$a.title = detectedLanguage?.toString?.() ?? 'Unknown language';
continue;
}
const [hrLangTagFrom, hrLangTagTo] = displayNames([detectedLanguage, navigator.language]);
$a.setAttribute('title', `${hrLangTagFrom} → ${hrLangTagTo}`);
async function onTranslate(event) {
event.preventDefault();
event.stopImmediatePropagation();
const $p = await translate(text, detectedLanguage);
$a.parentElement.insertAdjacentElement('afterend', $p);
}
$a.addEventListener('click', onTranslate, { once: true });
}
setTimeout(main, 2500);
}
// https://developer.mozilla.org/docs/Web/API/Translator
async function translate(text, from, to) {
const element = document.createElement('p');
function onProgress(event) {
const progress = (event.loaded * 100).toFixed(2);
element.textContent = `Downloaded: ${progress}%`;
}
try {
const availability = await findAvailableLanguage(from, to);
availability.monitor = m => m.addEventListener('downloadprogress', onProgress);
const translator = await Translator.create(availability);
const stream = translator.translateStreaming(text);
element.textContent = '';
for await (const chunk of stream)
element.textContent += chunk;
} catch (error) {
element.textContent = `ERROR: ${error.message}`;
}
return element;
}
async function findAvailableLanguage(sourceLanguage, targetLanguages) {
targetLanguages ??= navigator.languages;
for (const targetLanguage of targetLanguages) {
const options = { sourceLanguage, targetLanguage };
options.availability = await Translator.availability(options);
if (options.availability !== 'unavailable') return options;
}
}
// https://developer.mozilla.org/docs/Web/API/LanguageDetector
async function detectLanguage(text) {
detectLanguage.detector ??= LanguageDetector.create();
const detector = await detectLanguage.detector.catch(e => e);
if (Error.isError(detector)) return detector;
const { detectedLanguage } = (await detector.detect(text))[0];
return detectedLanguage === 'und' ? null : detectedLanguage;
}
function displayNames(codes, options={ type: 'language' }) {
const displayNames = new Intl.DisplayNames([navigator.language], options);
return codes.map(code => displayNames.of(code));
}
main();
@flipeador
Copy link
Author

flipeador commented May 21, 2025

Bluesky Translate

Translate Bluesky posts using the Translator API, available from Chrome 138 stable.

Note

  • Hover over the Translate link to see the current state.
  • The Translate link restores its default behavior after the first click.
  • You may have to wait a few seconds for LanguageDetector to download.

Installation

  1. Install the Tampermonkey browser extension.
  2. Import the script from the extension Dashboard.

@flipeador
Copy link
Author

You might have to enable Experimental translation API in Experimental features.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment