Usuari:Forat Negre/softcatala-traductor.js

Nota: Després de desar, heu de netejar la memòria cau del navegador per veure els canvis. En la majoria de navegadors amb Windows o Linux, premeu Ctrl+F5 o bé premeu Shift i cliqueu el botó "Actualitza" (Ctrl i "Actualitza" amb Internet Explorer). Vegeu més informació i instruccions per a cada navegador a Viquipèdia:Neteja de la memòria cau.

//<nowiki>
/* softcatala-traductor.js
--------------------------------
  Versió actual: 1.2
  Autors: Forat Negre, Wecoc
--------------------------------
  >> Introducció
  Aquest codi afegeix un botó "Softcatalà" a la pàgina de traducció d'articles.
  Utilitza el traductor neuronal de Softcatalà per traduir cada paràgraf.
  Podeu trobar el traductor al següent enllaç
    https://www.softcatala.org/traductor/

  >> Registre de canvis
  Versió 1.0 [14 de juliol 2022]
  * Botó principal afegit a la barra dreta de la pàgina
  * Traducció disponible en tots els idiomes del traductor Softcatalà

  Versió 1.1 [16 de juliol 2022]
  * Solucionat error intern d'obtenció del paràgraf actual

  Versió 1.2 [19 de juliol 2022]
  * Traducció bàsica de format implementada [negreta, enllaços, referències...]
  * Afegit mode "Testing mode" per poder fer proves de traducció sense risc
  * Solucionat error intern de memòria cau

  >> Coses pendents / Errors
  * Només permet traducció si tens activat "Copia el contingut original"

*/

/* Carregar el codi només a la pàgina de traduccions */
$(function() {
  setTimeout(function() {
    if (window.location.href.includes("Special:ContentTranslation")) {
      importSoftcatalaButton();
    }
  }, 5000);
});

function importSoftcatalaButton() {
  // ------------------------------------------------------
  // Defineix aquí si vols utilitzar el mode de proves
  // En fer-ho, elimina el botó "Publica" i mostra les funcions emprades a la consola
  var testingMode = false;
  // ------------------------------------------------------
  if (testingMode) {
    console.log("Import Softcatalà Button");
    document.querySelector('.ve-ui-toolbar-group-publish').style.display = "none";
  }
  /* Definir CSS personalitzat */
  var css = '.softcatala_button {' +
'   background: #777;' +
'   color: #bbb;' +
'   cursor: pointer;' +
'   user-select: none;' +
'   padding: 10px;' +
'   font-weight: 700;' +
'   font-size: 16px;' +
' }' +
' .softcatala_button.active {' +
'   background: #c81e2b;' +
'   color: #fff;' +
' }' +
' .softcatala_button.active:hover {' +
'   background: #e7414e;' +
' }' +
' .softcatala_button.disabled, .ve-cx-toolbar-mt .ve-cx-toolbar-mt-title {' +
'   display: none;' +
' }';

  /* Importar el CSS a la pàgina actual */
  var head = document.head || document.getElementsByTagName('head')[0],
    style = document.createElement('style');
  style.type = 'text/css';
  style.appendChild(document.createTextNode(css));
  head.appendChild(style);

  /* Crear botó Softcatalà */
  var toolbar = document.querySelector('.ve-cx-toolbar-mt'); if (!toolbar) return;
  var softcatala_button = document.createElement("div");
  $(softcatala_button).addClass("softcatala_button");
  softcatala_button.innerText = "Softcatalà";
  toolbar.insertBefore(softcatala_button, toolbar.firstChild);

  /* Si el traductor no té la llengua disponible, desactivar el botó */
  var sourceLanguage = mw.cx.sourceLanguage, targetLanguage = mw.cx.targetLanguage;

  var availableLangs = {
    /* Principals */
    'ca':'cat',     // Català
    'en':'en',      // Anglès
    'de':'deu',     // Alemany
    'oc':'oc',      // Occità
    'ro':'ron',     // Romanès
    'fr':'fr',      // Francès
    'pt':'pt',      // Portuguès
    'an':'arg',     // Aragonès
    'nl':'nld',     // Neerlandès
    'it':'ita',     // Italià
    /* Altres */
    'es':'spa',     // Espanyol
    'simple':'en'   // Anglès simplificat
  };

  sourceLanguage = availableLangs[sourceLanguage];
  targetLanguage = availableLangs[targetLanguage];
  if (!sourceLanguage || !targetLanguage) {
    softcatala_button.addClass("disabled");
  }

  /* Obtenir el traductor seleccionat */
  function readCurrentTranslator() {
    var c = document.querySelector('.oo-ui-toolGroup-tools .oo-ui-tool-active');
    var veTarget = mw.cx.tools.IssueTrackingTool.prototype.getVeTarget();
    var tools = Object.keys(veTarget.actionsToolbar.target.mtToolbar.tools);
    for (var t in tools) {
      if (c.classList.contains("oo-ui-tool-name-" + tools[t])) return tools[t];
    }
    return undefined;
  }

  /* Activar el botó només en mode "Copia el contingut original" */
  function updateSoftcatalaButton() {
    setTimeout(function() {
      var button = document.querySelector('.softcatala_button');
      if (readCurrentTranslator() == "source") {
        $(button).addClass("active");
      } else {
        $(button).removeClass("active");
      }
      updateSoftcatalaButton();
    }, 100);
  }
  updateSoftcatalaButton();

  /* Funció de clicar el botó */
  softcatala_button.addEventListener("click", function(e) {
    /* Si el botó està desactivat o inactiu, no fer res */
    if (this.classList.contains("disabled")) return;
    if (readCurrentTranslator() != "source") return;

    /* Anar a la secció del text corresponent */
    var activeNodes = document.querySelectorAll('.ve-ce-activeNode-active'),
      targetNode = activeNodes[activeNodes.length - 1],
      sectionId = targetNode.id,
      sectionNumber = sectionId.match(/cxTargetSection(\d*)/)[1],
      sourceNode = document.getElementById("cxSourceSection" + sectionNumber),
      neuronal_json_url = 'https://api.softcatala.org/v2/nmt/translate/';

    /* Obtenir el contingut a traduir per cada paràgraf */
    var sourceChildren = sourceNode.firstChild.children;
    var targetChildren = targetNode.firstChild.children;
    var blockMode = true;
    if (sourceChildren.length == targetChildren.length) {
      if (targetChildren.length > 0) {
        if (targetChildren[0].className.includes("cx-segment")) {
          blockMode = false;
        }
      }
    }
    /* Si el nombre de paràgrafs coincideix, fer-ho per parts */
    var waitForTranslation = [];
    if (!blockMode) {
      for (var i = 0; i < sourceChildren.length; i++) {
        waitForTranslation.push(i);
        applySoftcatalaTranslation(i, sourceChildren[i].cloneNode(true), blockMode);
      }
    /* Si el nombre de paràgrafs no coincideix, aplicar en un únic bloc */
    } else {
      waitForTranslation.push(0);
      applySoftcatalaTranslation(0, sourceNode.cloneNode(true), blockMode);
    }
    /* Up cop feta la traducció, guardar-la a la memòria cau */
    setTimeout(saveTranslationToQueue(), 200);

    /* Obtenir traducció de SoftCatalà mitjançant Ajax */
    function applySoftcatalaTranslation(spanIndex, source, blockMode) {
      if (testingMode) console.log("Apply Softcatala Translation");
      if (blockMode) {
        var blocks = source.firstChild.children;
        for (var i = 0; i < blocks.length; i++) {
          filterFormatFromSource(blocks[i]);
        }
      } else {
        filterFormatFromSource(source);
      }
      var content = source.innerText;
      $.ajax({
        url: neuronal_json_url,
        type: "POST",
        data : {
          'langpair': sourceLanguage + "|" + targetLanguage,
          'q': content,
          'savetext': false
        },
        dataType: 'json',
        spanIndex: spanIndex,
        success : function(data) {
          if (data.responseStatus == 200) {
            var result = data.responseData.translatedText, target;
            if (blockMode) {
              target = targetNode.firstChild;
            } else {
              target = targetChildren[this.spanIndex];
            }
            applyFormatFromProvider(result, "Apertium", target, this.spanIndex, blockMode);
          }
        }
      });
    }
    /* Versió de l'Ajax lliure per traduir text independent de l'article */
    function applySoftcatalaTranslationText(content) {
      if (testingMode) console.log("Apply Softcatala Translation Text");
      return $.ajax({
        url: neuronal_json_url,
        type: "POST",
        data : {
          'langpair': sourceLanguage + "|" + targetLanguage,
          'q': content,
          'savetext': false
        },
        dataType: 'json',
      });
    }
    /* Guardar els canvis a la memòria cau */
    function saveTranslationToQueue() {
       setTimeout(function() {
         if (waitForTranslation.length == 0) {
           if (testingMode) console.log("Save Translation To Queue");
           var veTarget = mw.cx.tools.IssueTrackingTool.prototype.getVeTarget();
           var section = veTarget.getTargetSectionNodeFromSectionNumber(sectionNumber);
           veTarget.setSectionContent(section, targetNode.outerHTML, "source");
           veTarget.emit('changeContentSource', sectionNumber);
         } else {
           saveTranslationToQueue();
         }
       }, 100);
    }
    /* Filtrar el format innecessari per la traducció (referències i altres elements) */
    function filterFormatFromSource(source) {
      if (testingMode) console.log("Filter Format From Source");
      var removePool = [];
      for (var i = 0; i < source.children.length; i++) {
        var child = source.children[i];
        var readableLocalNames = ["b", "i", "a"];
        if (!readableLocalNames.includes(child.localName)) {
          removePool.push(child);
        }
      }
      for (var r in removePool) { source.removeChild(removePool[r]); }
    }
    /* Aplicar el format utilitzant la traducció d'Apertium de referència, ja que Softcatalà no té aquesta opció */
    function applyFormatFromProvider(content, provider, target, spanIndex, blockMode) {
      if (testingMode) console.log("Apply Format From Provider");
      var veTarget = mw.cx.tools.IssueTrackingTool.prototype.getVeTarget(), result = {content: content};
      veTarget.translateSection("cxSourceSection" + sectionNumber, "Apertium").then(function(data) {
        var segments = $('.cx-segment', data), s, c, currentSegment;
        if (!blockMode) {
          if (segments.length > 1) {
            currentSegment = segments[spanIndex];
          } else {
            currentSegment = segments[0];
          }
          if (currentSegment !== undefined) {
            for (c = 0; c < currentSegment.children.length; c++) {
              applyFormatToChild(currentSegment.children[c], result);
            }
          }
        } else {
          for (s = 0; s < segments.length; s++) {
            for (c = 0; c < segments[s].children.length; c++) {
              applyFormatToChild(segments[s].children[c], result);
            }
          }
        }
        setTimeout(function() {
          result.content += " ";
          target.innerHTML = result.content;
          waitForTranslation.splice(waitForTranslation.indexOf(spanIndex));
        }, 1000);
      });
    }
    /* Aplicar el format cas per cas */
    function applyFormatToChild(child, result) {
      var readableLocalNames = ["b", "i", "a"];
      if (readableLocalNames.includes(child.localName)) {
        locateTargetFormat(result, child);
      } else {
        result.content += child.outerHTML;
      }
    }
    /* Localitzar estils de text */
    function locateTargetFormat(result, translatedSource) {
      if (testingMode) console.log("Locate Target Format");
      var source = document.getElementById(translatedSource.id);
      applySoftcatalaTranslationText(source.innerText).then(function(data) {
        if (data.responseStatus == 200) {
          var newText = data.responseData.translatedText;
          var regex = new RegExp("(\\s|^)(\\w*(" + newText + ")\\w*)(\\s|$)", "im");
          var match = result.content.match(regex);
          if (match) {
            var matchedText = match[2];
            translatedSource.innerText = matchedText;
            result.content = result.content.replace(matchedText, translatedSource.outerHTML);
          }
        }
      });
    }
  });
}
//</nowiki>