localization.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. // localization = {} -- the dict with translations is created by the backend
  2. var ignore_ids_for_localization = {
  3. setting_sd_hypernetwork: 'OPTION',
  4. setting_sd_model_checkpoint: 'OPTION',
  5. modelmerger_primary_model_name: 'OPTION',
  6. modelmerger_secondary_model_name: 'OPTION',
  7. modelmerger_tertiary_model_name: 'OPTION',
  8. train_embedding: 'OPTION',
  9. train_hypernetwork: 'OPTION',
  10. txt2img_styles: 'OPTION',
  11. img2img_styles: 'OPTION',
  12. setting_random_artist_categories: 'OPTION',
  13. setting_face_restoration_model: 'OPTION',
  14. setting_realesrgan_enabled_models: 'OPTION',
  15. extras_upscaler_1: 'OPTION',
  16. extras_upscaler_2: 'OPTION',
  17. };
  18. var re_num = /^[.\d]+$/;
  19. var re_emoji = /[\p{Extended_Pictographic}\u{1F3FB}-\u{1F3FF}\u{1F9B0}-\u{1F9B3}]/u;
  20. var original_lines = {};
  21. var translated_lines = {};
  22. function hasLocalization() {
  23. return window.localization && Object.keys(window.localization).length > 0;
  24. }
  25. function textNodesUnder(el) {
  26. var n, a = [], walk = document.createTreeWalker(el, NodeFilter.SHOW_TEXT, null, false);
  27. while ((n = walk.nextNode())) a.push(n);
  28. return a;
  29. }
  30. function canBeTranslated(node, text) {
  31. if (!text) return false;
  32. if (!node.parentElement) return false;
  33. var parentType = node.parentElement.nodeName;
  34. if (parentType == 'SCRIPT' || parentType == 'STYLE' || parentType == 'TEXTAREA') return false;
  35. if (parentType == 'OPTION' || parentType == 'SPAN') {
  36. var pnode = node;
  37. for (var level = 0; level < 4; level++) {
  38. pnode = pnode.parentElement;
  39. if (!pnode) break;
  40. if (ignore_ids_for_localization[pnode.id] == parentType) return false;
  41. }
  42. }
  43. if (re_num.test(text)) return false;
  44. if (re_emoji.test(text)) return false;
  45. return true;
  46. }
  47. function getTranslation(text) {
  48. if (!text) return undefined;
  49. if (translated_lines[text] === undefined) {
  50. original_lines[text] = 1;
  51. }
  52. var tl = localization[text];
  53. if (tl !== undefined) {
  54. translated_lines[tl] = 1;
  55. }
  56. return tl;
  57. }
  58. function processTextNode(node) {
  59. var text = node.textContent.trim();
  60. if (!canBeTranslated(node, text)) return;
  61. var tl = getTranslation(text);
  62. if (tl !== undefined) {
  63. node.textContent = tl;
  64. }
  65. }
  66. function processNode(node) {
  67. if (node.nodeType == 3) {
  68. processTextNode(node);
  69. return;
  70. }
  71. if (node.title) {
  72. let tl = getTranslation(node.title);
  73. if (tl !== undefined) {
  74. node.title = tl;
  75. }
  76. }
  77. if (node.placeholder) {
  78. let tl = getTranslation(node.placeholder);
  79. if (tl !== undefined) {
  80. node.placeholder = tl;
  81. }
  82. }
  83. textNodesUnder(node).forEach(function(node) {
  84. processTextNode(node);
  85. });
  86. }
  87. function localizeWholePage() {
  88. processNode(gradioApp());
  89. function elem(comp) {
  90. var elem_id = comp.props.elem_id ? comp.props.elem_id : "component-" + comp.id;
  91. return gradioApp().getElementById(elem_id);
  92. }
  93. for (var comp of window.gradio_config.components) {
  94. if (comp.props.webui_tooltip) {
  95. let e = elem(comp);
  96. let tl = e ? getTranslation(e.title) : undefined;
  97. if (tl !== undefined) {
  98. e.title = tl;
  99. }
  100. }
  101. if (comp.props.placeholder) {
  102. let e = elem(comp);
  103. let textbox = e ? e.querySelector('[placeholder]') : null;
  104. let tl = textbox ? getTranslation(textbox.placeholder) : undefined;
  105. if (tl !== undefined) {
  106. textbox.placeholder = tl;
  107. }
  108. }
  109. }
  110. }
  111. function dumpTranslations() {
  112. if (!hasLocalization()) {
  113. // If we don't have any localization,
  114. // we will not have traversed the app to find
  115. // original_lines, so do that now.
  116. localizeWholePage();
  117. }
  118. var dumped = {};
  119. if (localization.rtl) {
  120. dumped.rtl = true;
  121. }
  122. for (const text in original_lines) {
  123. if (dumped[text] !== undefined) continue;
  124. dumped[text] = localization[text] || text;
  125. }
  126. return dumped;
  127. }
  128. function download_localization() {
  129. var text = JSON.stringify(dumpTranslations(), null, 4);
  130. var element = document.createElement('a');
  131. element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
  132. element.setAttribute('download', "localization.json");
  133. element.style.display = 'none';
  134. document.body.appendChild(element);
  135. element.click();
  136. document.body.removeChild(element);
  137. }
  138. document.addEventListener("DOMContentLoaded", function() {
  139. if (!hasLocalization()) {
  140. return;
  141. }
  142. onUiUpdate(function(m) {
  143. m.forEach(function(mutation) {
  144. mutation.addedNodes.forEach(function(node) {
  145. processNode(node);
  146. });
  147. });
  148. });
  149. localizeWholePage();
  150. if (localization.rtl) { // if the language is from right to left,
  151. (new MutationObserver((mutations, observer) => { // wait for the style to load
  152. mutations.forEach(mutation => {
  153. mutation.addedNodes.forEach(node => {
  154. if (node.tagName === 'STYLE') {
  155. observer.disconnect();
  156. for (const x of node.sheet.rules) { // find all rtl media rules
  157. if (Array.from(x.media || []).includes('rtl')) {
  158. x.media.appendMedium('all'); // enable them
  159. }
  160. }
  161. }
  162. });
  163. });
  164. })).observe(gradioApp(), {childList: true});
  165. }
  166. });