text-processor.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. // text-processor.js
  2. import axios from 'axios'
  3. import { delayPerform } from 'js-perform-lock'
  4. const WindowsTranslateApi = 'https://jiasm.zfire.top/translate'
  5. // 数据过滤校验
  6. function isNumberRegex(str) {
  7. const regexPatterns = [
  8. /^\d+$/, // 纯数字
  9. /^\d*\.\d+$/, // 浮点数
  10. /^([01]\d|2[0-3]):([0-5]\d)$/, // 时间 (HH:MM)
  11. /^([01]\d|2[0-3]):([0-5]\d):([0-5]\d)$/, // 时间 (HH:MM:SS)
  12. /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/, // 完整时间戳
  13. /^[A-Za-z]+$/, // 纯字母
  14. /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]+$/, // 字母和数字
  15. /^(?=.*[A-Za-z])(?=.*[\p{P}\p{S}])[\p{L}\p{P}\p{S}]+$/u, // 字母和标点符号
  16. /^(?=.*\d)(?=.*[\p{P}\p{S}])[\d\p{P}\p{S}]+$/u, // 数字和标点符号
  17. /^[\p{P}\s\dA-Za-z]+$/u // 字母、数字和标点符号
  18. ]
  19. return regexPatterns.some(pattern => pattern.test(str)) || !!window.Vue_Translation_Of_Text_Old_Data?.[str]
  20. }
  21. // 文本转译请求
  22. function pushNewText(data) {
  23. return new Promise(function (resolve, reject) {
  24. axios
  25. .post(`${WindowsTranslateApi}/api/v1/common/translationOfText`, data)
  26. .then(response => {
  27. if (response.data.code === 0) {
  28. const obj = {}
  29. const obj2 = {}
  30. response.data.data.forEach(val => {
  31. obj[val[0]] = val[1]
  32. obj2[val[1]] = true
  33. })
  34. window.Vue_Translation_Of_Text_Old_Data = {
  35. ...(window.Vue_Translation_Of_Text_Old_Data || {}),
  36. ...obj2
  37. }
  38. window.Vue_Translation_Of_Text_Data = {
  39. ...(window.Vue_Translation_Of_Text_Data || {}),
  40. ...obj
  41. }
  42. resolve(obj)
  43. } else {
  44. reject(new Error('API returned non-zero code'))
  45. }
  46. })
  47. .catch(reject)
  48. })
  49. }
  50. // 延迟替换处理器
  51. const deferredReplacement = (function () {
  52. const allKeywords = []
  53. const keywords = []
  54. const callbacks = []
  55. let keywordIndex = 0
  56. let callbackIndex = 0
  57. const delayProcessor = new delayPerform(250).refactor(function () {
  58. const currentKeywords = keywords.splice(0, keywordIndex)
  59. const currentCallbacks = callbacks.splice(0, callbackIndex)
  60. keywordIndex -= currentKeywords.length
  61. callbackIndex -= currentCallbacks.length
  62. if (currentKeywords.length > 0) {
  63. pushNewText({
  64. keywords: currentKeywords,
  65. targetLanguage: window?.Vue_Translation_Of_Text_Type
  66. }).then(() => {
  67. currentCallbacks.forEach(cb => cb?.())
  68. }).catch(error => {
  69. console.error('Translation failed:', error)
  70. currentCallbacks.forEach(cb => cb?.())
  71. })
  72. } else if (currentCallbacks.length > 0) {
  73. setTimeout(() => {
  74. currentCallbacks.forEach(cb => cb?.())
  75. }, 250)
  76. }
  77. })
  78. return function (text, callback) {
  79. delayProcessor()
  80. if (!allKeywords.includes(text)) {
  81. keywordIndex++
  82. allKeywords.push(text)
  83. keywords.push(text)
  84. }
  85. callbackIndex++
  86. callbacks.push(callback)
  87. }
  88. })()
  89. // DOM 处理器
  90. export const domProcessor = {
  91. // 处理文本节点
  92. processTextNode(node) {
  93. const text = node.textContent?.trim()
  94. if (!text || isNumberRegex(text)) return
  95. try {
  96. if (window.Vue_Translation_Of_Text_Data?.[text]) {
  97. node.textContent = window.Vue_Translation_Of_Text_Data[text]
  98. } else {
  99. deferredReplacement(text, () => {
  100. if (window.Vue_Translation_Of_Text_Data?.[text]) {
  101. node.textContent = window.Vue_Translation_Of_Text_Data[text]
  102. }
  103. })
  104. }
  105. } catch (error) {
  106. console.error('Text processing error:', error)
  107. }
  108. },
  109. // 处理输入框 placeholder
  110. processInputPlaceholder(element) {
  111. const placeholder = element.getAttribute('placeholder')?.trim()
  112. if (!placeholder || isNumberRegex(placeholder)) return
  113. try {
  114. if (window.Vue_Translation_Of_Text_Data?.[placeholder]) {
  115. element.setAttribute('placeholder', window.Vue_Translation_Of_Text_Data[placeholder])
  116. } else {
  117. deferredReplacement(placeholder, () => {
  118. if (window.Vue_Translation_Of_Text_Data?.[placeholder]) {
  119. element.setAttribute('placeholder', window.Vue_Translation_Of_Text_Data[placeholder])
  120. }
  121. })
  122. }
  123. } catch (error) {
  124. console.error('Placeholder processing error:', error)
  125. }
  126. },
  127. // 处理输入框值
  128. processInputValue(element) {
  129. const value = element.value?.trim()
  130. if (!value || isNumberRegex(value)) return
  131. try {
  132. if (window.Vue_Translation_Of_Text_Data?.[value]) {
  133. element.value = window.Vue_Translation_Of_Text_Data[value]
  134. } else {
  135. deferredReplacement(value, () => {
  136. if (window.Vue_Translation_Of_Text_Data?.[value]) {
  137. element.value = window.Vue_Translation_Of_Text_Data[value]
  138. }
  139. })
  140. }
  141. } catch (error) {
  142. console.error('Input value processing error:', error)
  143. }
  144. },
  145. // 处理属性文本
  146. processAttributeText(element, attributeName) {
  147. const value = element.getAttribute(attributeName)?.trim()
  148. if (!value || isNumberRegex(value)) return
  149. try {
  150. if (window.Vue_Translation_Of_Text_Data?.[value]) {
  151. element.setAttribute(attributeName, window.Vue_Translation_Of_Text_Data[value])
  152. } else {
  153. deferredReplacement(value, () => {
  154. if (window.Vue_Translation_Of_Text_Data?.[value]) {
  155. element.setAttribute(attributeName, window.Vue_Translation_Of_Text_Data[value])
  156. }
  157. })
  158. }
  159. } catch (error) {
  160. console.error(`Attribute ${attributeName} processing error:`, error)
  161. }
  162. },
  163. // 递归处理 DOM 元素
  164. processElement(element) {
  165. if (element && element.nodeType === Node.ELEMENT_NODE) {
  166. // 跳过 table 中的 td 元素
  167. // if (element.nodeName.toLowerCase() === 'td') {
  168. // return
  169. // }
  170. // 处理输入框和文本域
  171. if (['INPUT', 'TEXTAREA', 'SELECT'].includes(element.nodeName)) {
  172. this.processInputPlaceholder(element)
  173. if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {
  174. this.processInputValue(element)
  175. }
  176. }
  177. // 处理 Ant Design 组件属性
  178. this.processAttributeText(element, 'title')
  179. this.processAttributeText(element, 'aria-label')
  180. this.processAttributeText(element, 'alt')
  181. // 处理子节点
  182. Array.from(element.childNodes).forEach(child => {
  183. if (child.nodeType === Node.TEXT_NODE) {
  184. this.processTextNode(child)
  185. } else {
  186. this.processElement(child)
  187. }
  188. })
  189. }
  190. },
  191. // 处理特定选择器元素
  192. processSelector(selector) {
  193. Array.from(document.querySelectorAll(selector)).forEach(element => {
  194. this.processElement(element)
  195. })
  196. },
  197. // 处理标签名元素
  198. processTagName(tagName) {
  199. Array.from(document.getElementsByTagName(tagName)).forEach(element => {
  200. this.processElement(element)
  201. })
  202. }
  203. }