BlueSky Dynamic Translation Display

Insert the translation result into the previous sibling element of the parent element when the translate button is clicked.

  1. // ==UserScript==
  2. // @name BlueSky Dynamic Translation Display
  3. // @namespace http://tampermonkey.net/
  4. // @version 3.0
  5. // @description Insert the translation result into the previous sibling element of the parent element when the translate button is clicked.
  6. // @author littelsix
  7. // @match https://bsky.app/*
  8. // @grant none
  9. // ==/UserScript==
  10.  
  11. (function () {
  12. 'use strict';
  13.  
  14. console.log('Script loaded successfully!');
  15.  
  16. const supportedLanguages = {
  17. 'zh-CN': '中文',
  18. 'en': 'English',
  19. 'es': 'Español',
  20. 'fr': 'Français',
  21. 'de': 'Deutsch',
  22. 'ja': '日本語'
  23. };
  24.  
  25. function detectDefaultLanguage() {
  26. const browserLanguage = navigator.language || navigator.languages[0];
  27. console.log(`Detected browser language${browserLanguage}`);
  28. return supportedLanguages[browserLanguage] ? browserLanguage : 'zh-CN';
  29. }
  30.  
  31. let userLanguage = localStorage.getItem('bsky-translate-language') || detectDefaultLanguage();
  32.  
  33. async function translateText(text, targetLang = 'zh-CN') {
  34. const apiUrl = `https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&tl=${targetLang}&dt=t&q=${encodeURIComponent(text)}`;
  35. try {
  36. const response = await fetch(apiUrl);
  37. if (!response.ok) {
  38. throw new Error(`HTTP error${response.status}`);
  39. }
  40. const result = await response.json();
  41. return result[0].map(segment => segment[0]).join('');
  42. } catch (error) {
  43. console.error(`Translation failed${error.message || 'Unable to connect to the translation service. Please try again later'}`);
  44. throw error;
  45. }
  46. }
  47.  
  48. function ensureTranslationContainer(referenceElement) {
  49. let translationDiv = referenceElement.querySelector('.translated-text');
  50. if (!translationDiv) {
  51. translationDiv = document.createElement('div');
  52. translationDiv.className = 'css-146c3p1 translated-text';
  53. translationDiv.style.color = 'rgb(16, 131, 254)';
  54. translationDiv.style.marginTop = '10px';
  55. translationDiv.textContent = 'Translation results will appear here';
  56. referenceElement.appendChild(translationDiv);
  57. }
  58. return translationDiv;
  59. }
  60.  
  61. function createLanguageSelector(button) {
  62. let languageSelector = button.parentElement.querySelector('.language-selector');
  63. if (!languageSelector) {
  64. languageSelector = document.createElement('select');
  65. languageSelector.className = 'language-selector';
  66. languageSelector.style.marginLeft = '10px';
  67. languageSelector.style.border = '1px solid #ccc';
  68. languageSelector.style.borderRadius = '4px';
  69. languageSelector.style.padding = '2px';
  70.  
  71. for (const [code, name] of Object.entries(supportedLanguages)) {
  72. const option = document.createElement('option');
  73. option.value = code;
  74. option.textContent = name;
  75. if (code === userLanguage) {
  76. option.selected = true;
  77. }
  78. languageSelector.appendChild(option);
  79. }
  80.  
  81. languageSelector.addEventListener('change', (event) => {
  82. userLanguage = event.target.value;
  83. localStorage.setItem('bsky-translate-language', userLanguage);
  84. console.log(`Language switched to${supportedLanguages[userLanguage]}`);
  85. });
  86.  
  87. button.parentElement.appendChild(languageSelector);
  88. }
  89. }
  90.  
  91. function bindTranslateButtons() {
  92. const translateButtons = document.querySelectorAll('a[href*="https://translate.google.com"]');
  93. console.log(`Found ${translateButtons.length} translate buttons.`);
  94.  
  95. translateButtons.forEach(button => {
  96. if (!button.dataset.bound) {
  97. button.dataset.bound = true;
  98.  
  99. createLanguageSelector(button);
  100.  
  101. button.addEventListener('click', async (event) => {
  102. event.preventDefault();
  103. event.stopPropagation();
  104. console.log('Translate button clicked!');
  105.  
  106. const parentElement = button.parentElement.parentElement;
  107. if (!parentElement) {
  108. console.error('Parent element not found。');
  109. return;
  110. }
  111.  
  112. const postTextDiv = parentElement.previousElementSibling;
  113. if (!postTextDiv) {
  114. console.error('Previous sibling element not found, unable to translate.');
  115. return;
  116. }
  117.  
  118. const textToTranslate = postTextDiv.textContent.trim();
  119. if (!textToTranslate) {
  120. console.error('Text is empty, unable to translate.');
  121. return;
  122. }
  123.  
  124. const translationDiv = ensureTranslationContainer(postTextDiv);
  125.  
  126. translationDiv.textContent = 'Translating...';
  127. try {
  128. const translatedText = await translateText(textToTranslate, userLanguage);
  129. console.log(`Original text${textToTranslate}`);
  130. console.log(`Translation result${translatedText}`);
  131. translationDiv.textContent = translatedText;
  132. translationDiv.dataset.translated = 'true';
  133. } catch (error) {
  134. translationDiv.textContent = 'Translation failed, please try again later.。';
  135. console.error('Translation failed. Check console logs for details.');
  136. }
  137. });
  138. }
  139. });
  140. }
  141.  
  142. bindTranslateButtons();
  143.  
  144. const observer = new MutationObserver(() => {
  145. bindTranslateButtons();
  146. });
  147.  
  148. const targetNode = document.body;
  149. const config = { childList: true, subtree: true };
  150. observer.observe(targetNode, config);
  151. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址