Persistent Translator Tooltip with Sidebar and Local Storage

Translate words on click, show in a tooltip, add to a sidebar list, and store in local storage

当前为 2023-12-06 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Persistent Translator Tooltip with Sidebar and Local Storage
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.0
  5. // @description Translate words on click, show in a tooltip, add to a sidebar list, and store in local storage
  6. // @include *://*/*
  7. // @grant GM_xmlhttpRequest
  8. // @license MIT
  9. // ==/UserScript==
  10.  
  11. (function() {
  12. 'use strict';
  13.  
  14. // Add CSS for the tooltip, sidebar, clear button, and entry layout
  15. const styleElement = document.createElement('style');
  16. styleElement.type = 'text/css';
  17. styleElement.innerHTML = `
  18. .translator-tooltip {
  19. font-weight: 700;
  20. color: #000000;
  21. position: absolute;
  22. z-index: 10000;
  23. padding: 2px;
  24. max-width: 300px;
  25. border-radius: 0.3em;
  26. background-color: #ffffdb;
  27. border: 1px solid #ccc;
  28. box-shadow: 0 2px 4px rgba(0,0,0,0.2);
  29. text-align: center;
  30. font-size: 18px;
  31. line-height: 1.2;
  32. visibility: hidden;
  33. opacity: 0;
  34. transition: visibility 0s linear 300ms, opacity 300ms;
  35. }
  36.  
  37. .translator-tooltip.visible {
  38. visibility: visible;
  39. opacity: 1;
  40. }
  41.  
  42. .translator-sidebar {
  43. position: fixed;
  44. top: 0;
  45. right: 0;
  46. width: 250px;
  47. height: 100%;
  48. background-color: #ffffdb;
  49. overflow-y: auto;
  50. border-left: 1px solid #ccc;
  51. padding: 10px;
  52. z-index: 10000;
  53. font-size: 16px;
  54. }
  55.  
  56. .translator-entry {
  57. display: flex;
  58. justify-content: space-between;
  59. align-items: center;
  60. margin-bottom: 5px;
  61. }
  62.  
  63. .translator-entry span:first-child {
  64. flex: 1;
  65. text-align: left;
  66. }
  67.  
  68. .translator-entry span:last-child {
  69. flex: 1;
  70. text-align: right;
  71. }
  72.  
  73. .translator-entry hr {
  74. width: 100%;
  75. margin-top: 5px;
  76. }
  77.  
  78. .clear-button {
  79. position: fixed;
  80. bottom: 20px;
  81. right: 20px;
  82. z-index: 10001;
  83. cursor: pointer;
  84. padding: 5px 10px;
  85. background-color: #f5f5f5;
  86. border: 1px solid #ccc;
  87. border-radius: 5px;
  88. box-shadow: 0 2px 4px rgba(0,0,0,0.2);
  89. font-size: 16px;
  90. }
  91. `;
  92. document.head.appendChild(styleElement);
  93.  
  94. // Create tooltip element
  95. const tooltip = document.createElement('div');
  96. tooltip.className = 'translator-tooltip';
  97. document.body.appendChild(tooltip);
  98.  
  99. // Create sidebar element
  100. const sidebar = document.createElement('div');
  101. sidebar.className = 'translator-sidebar';
  102. document.body.appendChild(sidebar);
  103.  
  104. // Create clear button element
  105. const clearButton = document.createElement('button');
  106. clearButton.innerHTML = '🗑️ Clear Translations';
  107. clearButton.className = 'clear-button';
  108. document.body.appendChild(clearButton);
  109.  
  110. // Function to show the tooltip
  111. function showTooltip(text, x, y) {
  112. tooltip.textContent = text;
  113. tooltip.style.left = `${x}px`;
  114. tooltip.style.top = `${y - 30}px`;
  115. tooltip.classList.add('visible');
  116. }
  117.  
  118. // Function to hide the tooltip
  119. function hideTooltip() {
  120. tooltip.classList.remove('visible');
  121. }
  122.  
  123. // Function to add word to sidebar list and store in local storage
  124. function addToSidebar(originalWord, translatedWord) {
  125. const entry = document.createElement('div');
  126. entry.className = 'translator-entry';
  127. entry.innerHTML = `
  128. <span>${originalWord}</span>
  129. <span>${translatedWord}</span>
  130. `;
  131.  
  132. sidebar.appendChild(entry);
  133.  
  134. // Add a horizontal line after each entry
  135. const separator = document.createElement('hr');
  136. sidebar.appendChild(separator);
  137.  
  138. // Store in local storage
  139. const translations = JSON.parse(localStorage.getItem('translations') || '{}');
  140. translations[originalWord] = translatedWord;
  141. localStorage.setItem('translations', JSON.stringify(translations));
  142. }
  143.  
  144. // Function to translate word
  145. function translateWord(word, x, y) {
  146. GM_xmlhttpRequest({
  147. method: "GET",
  148. url: "https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&tl=fa&dj=1&dt=t&dt=rm&q=" + encodeURIComponent(word),
  149. onload: function(response) {
  150. const data = JSON.parse(response.responseText);
  151. const translatedText = data.sentences[0].trans;
  152. showTooltip(translatedText, x, y);
  153. addToSidebar(word, translatedText);
  154. }
  155. });
  156. }
  157.  
  158. // Load translations from local storage and add to sidebar
  159. function loadTranslations() {
  160. const translations = JSON.parse(localStorage.getItem('translations') || '{}');
  161. Object.keys(translations).forEach(word => {
  162. const translatedWord = translations[word];
  163. if (typeof translatedWord === 'string') {
  164. const entry = document.createElement('div');
  165. entry.className = 'translator-entry';
  166. entry.innerHTML = `
  167. <span>${word}</span>
  168. <span>${translatedWord}</span>
  169. `;
  170. sidebar.appendChild(entry);
  171.  
  172. // Add a horizontal line after each entry
  173. const separator = document.createElement('hr');
  174. sidebar.appendChild(separator);
  175. }
  176. });
  177. }
  178.  
  179. // Function to clear translations from sidebar and local storage
  180. function clearTranslations() {
  181. sidebar.innerHTML = '';
  182. localStorage.removeItem('translations');
  183. }
  184.  
  185. // Event listener for clear button
  186. clearButton.addEventListener('click', function() {
  187. clearTranslations();
  188. });
  189.  
  190. // Event listener for mouseup to detect text selection
  191. document.addEventListener('mouseup', function(event) {
  192. const selection = window.getSelection().toString().trim();
  193. if (selection) { // Remove the condition that checks for a single word
  194. const rect = window.getSelection().getRangeAt(0).getBoundingClientRect();
  195. translateWord(selection, rect.left + window.scrollX, rect.top + window.scrollY);
  196. }
  197. });
  198.  
  199. // Event listener to hide tooltip when clicking anywhere on the page
  200. document.addEventListener('mousedown', function(event) {
  201. if (!tooltip.contains(event.target)) {
  202. hideTooltip();
  203. }
  204. }, true);
  205.  
  206. // Load stored translations on page load
  207. loadTranslations();
  208. })();