Difference between revisions of "User:Dmy"

(Created page with "Hello! I'm Dmy. I'm mainly contributing to the Ukrainian translation. Category:Translators")
 
(added my userscript (that handles some common replacements for Ukrainian), maybe someone else would find it useful)
 
Line 1: Line 1:
 
Hello! I'm Dmy. I'm mainly contributing to the Ukrainian translation.
 
Hello! I'm Dmy. I'm mainly contributing to the Ukrainian translation.
 +
 +
== Скрипт для спрощення українських перекладів ==
 +
 +
Я використовую наступний користувацький скрипт, який заміняє часті слова в перекладі автоматично. Щоб встановити його, використовуйте доповнення для браузеру Greasemonkey (для Firefox) або Tampermonkey (для Chromium).
 +
 +
Після встановлення цього скрипта на сторніках редагування з'явиться кнопка «Підготвувати переклад». Вона автоматично скопіює текст з англійської сторінки, замінить деякі часті слова і деякі посилання. Використовуйте її тільки для нових сторінок українською мовою. '''Скрипт в бета-версії і заміняє не все''', однак, хочеться вірити, корисний.
 +
 +
<pre>
 +
// ==UserScript==
 +
// @name    Löve wiki Ukrainian helper
 +
// @version  1
 +
// @grant    none
 +
// @match https://love2d.org/w/index.php?*
 +
// ==/UserScript==
 +
 +
function getGetParams() {
 +
  const params = {};
 +
  location.search.substr(1).split('&').map(part => {
 +
    const [key, value] = part.split('=');
 +
    params[key] = decodeURIComponent(value);
 +
  });
 +
  return params;
 +
}
 +
 +
async function getPageWikitext(pageName) {
 +
  const url = `https://love2d.org/w/api.php?action=parse&page=${encodeURIComponent(pageName)}&prop=wikitext&formatversion=2&format=json`;
 +
  console.log(url);
 +
 +
  const response = await fetch(url);
 +
  const { parse: { wikitext } } = await response.json();
 +
  return wikitext;
 +
}
 +
 +
function getEnglishPageName(getParams = null) {
 +
  const title = (getParams || getGetParams()).title;
 +
  const matched = title.match(/^(.*)_\(Українська\)$/);
 +
  if (matched) {
 +
    const [matchedString, pageName, suffix] = matched;
 +
    return pageName;
 +
  }
 +
 +
  return null;
 +
}
 +
 +
function isSemanticLink(link) {
 +
  return Boolean(link.match(/::/));
 +
}
 +
 +
function makeSemanticLink(originalLink) {
 +
  const [fullLink, semanticPrefix, page] = originalLink.match(/^(.*)::(.*)$/);
 +
 +
  return `[[${semanticPrefix}::${page} (Українська)|${page}]]`;
 +
}
 +
 +
function isNamespacedLink(link) {
 +
  const namespaces = ['Category', 'Category talk', 'Concept', 'Concept talk', 'File', 'File talk', 'Help', 'Help talk', 'Media', 'MediaWiki', 'MediaWiki talk', 'Project', 'Project talk', 'Property', 'Property talk', 'Rule', 'Rule talk', 'Special', 'Talk', 'Template', 'Template talk', 'Tutorial', 'Tutorial talk', 'User', 'User talk', 'smw/schema', 'smw/schema talk'];
 +
 +
  const matches = link.match(/^(.*):(.*)$/);
 +
  if (matches) {
 +
    const [full, ns, page] = matches;
 +
    return namespaces.includes(ns.trim());
 +
  }
 +
}
 +
 +
function makeTranslatedLink(link) {
 +
  return '{{translated link|' + link + '|Українська}}';
 +
}
 +
 +
function isNumericLink(link) {
 +
    return Boolean(link.match(/^[0-9.]+$/));
 +
}
 +
 +
function preprocessLink(fullString, innerPart) {
 +
  console.log('looking at this link: ' + innerPart);
 +
  if (fullString.match(/\(Українська\)/)) {
 +
    return fullString;
 +
  }
 +
 +
  if (isSemanticLink(innerPart)) {
 +
    return makeSemanticLink(innerPart);
 +
  } else if (!isNamespacedLink(innerPart) && !isNumericLink(innerPart)) {
 +
    return makeTranslatedLink(innerPart);
 +
  }
 +
 +
  return fullString;
 +
}
 +
 +
function preprocessAskArgumentLink(fullLink, innerText) {
 +
  //TODO
 +
}
 +
 +
function preprocessAskCall(fullCall, argumentString) {
 +
  const arguments = argumentString.split('\n|').map(x => x.trim());
 +
 +
  const namedArguments = {};
 +
  arguments.forEach(x => {
 +
    const parts = x.split('=');
 +
    if (parts.length == 2) {
 +
      const [key, value] = parts;
 +
      namedArguments[key.trim()] = value.trim();
 +
    }
 +
  });
 +
 +
  if (namedArguments.template !== 'ListingFields') {
 +
    return fullCall;
 +
  }
 +
 +
  // TODO: replace [[...]]
 +
 +
  const templateIndex = arguments.findIndex((x) => Boolean(x.match(/^\s*template\s*=.*$/)));
 +
  arguments[templateIndex] = 'template=ListingFields/with custom links';
 +
 +
  const firstFieldIndex = arguments.findIndex((x) => Boolean(x.match(/^\s*\?/)))
 +
  arguments.splice(firstFieldIndex, 0, '?Link for lists');
 +
 +
  return '{{#ask: ' + arguments.join('\n| ') + '\n}}';
 +
}
 +
 +
function preprocessText(s, englishPageName) {
 +
  const replacements = [
 +
    [/\{\{(newin|oldin|deprecatedin|param)\|/g, '{{$1 (Українська)|'],
 +
    [/(=+\s*)Constructor(\s*=+)/g, '$1Конструктор$2'],
 +
    [/(=+\s*)Constructors(\s*=+)/g, '$1Конструктори$2'],
 +
    [/(=+\s*)Function(\s*=+)/g, '$1Функція$2'],
 +
    [/(=+\s*)Functions(\s*=+)/g, '$1Функції$2'],
 +
    [/(=+\s*)Synopsis(\s*=+)/g, '$1Вигляд$2'],
 +
    [/(=+\s*)Argument(\s*=+)/g, '$1Аргумент$2'],
 +
    [/(=+\s*)Arguments(\s*=+)/g, '$1Аргументи$2'],
 +
    [/(=+\s*)Returns(\s*=+)/g, '$1Повертає$2'],
 +
    [/(=+\s*)Examples(\s*=+)/g, '$1Приклади$2'],
 +
    [/(=+\s*)Example(\s*=+)/g, '$1Приклад$2'],
 +
    [/(=+\s*)See Also(\s*=+)/g, '$1Див. також$2'],
 +
    [/(=+\s*)Supertypes(\s*=+)/g, '$1Базові типи$2'],
 +
    [/(=+\s*)Supertype(\s*=+)/g, '$1Базовий тип$2'],
 +
    [/(=+\s*)Other Languages(\s*=+)/g, '$1Іншими мовами$2'],
 +
    [/(=+\s*)Constants(\s*=+)/g, '$1Константи$2'],
 +
   
 +
    // This might not always be a correct translation, but it's so common and it's correct in 90% cases so it's worth including it here
 +
    [/\nNone.\n/g, '\nВідсутні.\n'],
 +
 +
    // if there is a duplicate (Українська) somewhere, it was probably introduced by our script, it shouldn't happen
 +
    [/\(Українська\) \(Українська\)/g, '(Українська)'],
 +
  ];
 +
 +
  let current = s;
 +
  replacements.forEach(([regexToReplace, replacementString]) => {
 +
    current = current.replaceAll(regexToReplace, replacementString);
 +
  });
 +
 +
  if (englishPageName && !current.match(/#set:Link for lists/)) {
 +
    current = current.trim() + "\n{{#set:Link for lists=[[" + englishPageName + " (Українська)|" + englishPageName + "]]}}";
 +
  }
 +
 +
  // we don't process pages with | in them, they need to be handled manually
 +
  current = current.replaceAll(/\[\[\s*([^\]|]+?)\s*\]\]/g, preprocessLink);
 +
 +
  current = current.replaceAll(/\{\{#ask:(.*)}}/gs, preprocessAskCall);
 +
 +
  // TODO: handle lists
 +
  return current;
 +
}
 +
 +
async function preparePageForTranslation(event) {
 +
  const textbox = document.getElementById('wpTextbox1');
 +
  if (textbox) {
 +
    let value = textbox.value;
 +
    const englishPageName = getEnglishPageName();
 +
 +
    if (englishPageName) {
 +
      if (value.trim() === '') {
 +
        value = await getPageWikitext(englishPageName);
 +
      }
 +
 +
      const newValue = preprocessText(value, englishPageName);
 +
      textbox.value = newValue;
 +
    }
 +
  }
 +
}
 +
 +
function addButton() {
 +
  const toolbar = document.getElementById('toolbar');
 +
  if (toolbar) {
 +
    const button = document.createElement('button');
 +
    button.innerText = 'Підготувати переклад';
 +
    button.addEventListener('click', (event) => {
 +
      event.preventDefault();
 +
      preparePageForTranslation();
 +
    });
 +
    toolbar.append(button);
 +
  }
 +
}
 +
 +
addButton();
 +
</pre>
  
 
[[Category:Translators]]
 
[[Category:Translators]]

Latest revision as of 13:55, 22 September 2023

Hello! I'm Dmy. I'm mainly contributing to the Ukrainian translation.

Скрипт для спрощення українських перекладів

Я використовую наступний користувацький скрипт, який заміняє часті слова в перекладі автоматично. Щоб встановити його, використовуйте доповнення для браузеру Greasemonkey (для Firefox) або Tampermonkey (для Chromium).

Після встановлення цього скрипта на сторніках редагування з'явиться кнопка «Підготвувати переклад». Вона автоматично скопіює текст з англійської сторінки, замінить деякі часті слова і деякі посилання. Використовуйте її тільки для нових сторінок українською мовою. Скрипт в бета-версії і заміняє не все, однак, хочеться вірити, корисний.

// ==UserScript==
// @name     Löve wiki Ukrainian helper
// @version  1
// @grant    none
// @match https://love2d.org/w/index.php?*
// ==/UserScript==

function getGetParams() {
  const params = {};
  location.search.substr(1).split('&').map(part => {
    const [key, value] = part.split('=');
    params[key] = decodeURIComponent(value);
  });
  return params;
}

async function getPageWikitext(pageName) {
  const url = `https://love2d.org/w/api.php?action=parse&page=${encodeURIComponent(pageName)}&prop=wikitext&formatversion=2&format=json`;
  console.log(url);

  const response = await fetch(url);
  const { parse: { wikitext } } = await response.json();
  return wikitext;
}

function getEnglishPageName(getParams = null) {
  const title = (getParams || getGetParams()).title;
  const matched = title.match(/^(.*)_\(Українська\)$/);
  if (matched) {
    const [matchedString, pageName, suffix] = matched;
    return pageName;
  }

  return null;
}

function isSemanticLink(link) {
  return Boolean(link.match(/::/));
}

function makeSemanticLink(originalLink) {
  const [fullLink, semanticPrefix, page] = originalLink.match(/^(.*)::(.*)$/);

  return `[[${semanticPrefix}::${page} (Українська)|${page}]]`;
}

function isNamespacedLink(link) {
  const namespaces = ['Category', 'Category talk', 'Concept', 'Concept talk', 'File', 'File talk', 'Help', 'Help talk', 'Media', 'MediaWiki', 'MediaWiki talk', 'Project', 'Project talk', 'Property', 'Property talk', 'Rule', 'Rule talk', 'Special', 'Talk', 'Template', 'Template talk', 'Tutorial', 'Tutorial talk', 'User', 'User talk', 'smw/schema', 'smw/schema talk'];

  const matches = link.match(/^(.*):(.*)$/);
  if (matches) {
    const [full, ns, page] = matches;
    return namespaces.includes(ns.trim());
  }
}

function makeTranslatedLink(link) {
  return '{{translated link|' + link + '|Українська}}';
}

function isNumericLink(link) {
    return Boolean(link.match(/^[0-9.]+$/));
}

function preprocessLink(fullString, innerPart) {
  console.log('looking at this link: ' + innerPart);
  if (fullString.match(/\(Українська\)/)) {
    return fullString;
  }

  if (isSemanticLink(innerPart)) {
    return makeSemanticLink(innerPart);
  } else if (!isNamespacedLink(innerPart) && !isNumericLink(innerPart)) {
    return makeTranslatedLink(innerPart);
  }

  return fullString;
}

function preprocessAskArgumentLink(fullLink, innerText) {
  //TODO
}

function preprocessAskCall(fullCall, argumentString) {
  const arguments = argumentString.split('\n|').map(x => x.trim());

  const namedArguments = {};
  arguments.forEach(x => {
    const parts = x.split('=');
    if (parts.length == 2) {
      const [key, value] = parts;
      namedArguments[key.trim()] = value.trim();
    }
  });

  if (namedArguments.template !== 'ListingFields') {
    return fullCall;
  }

  // TODO: replace [[...]]

  const templateIndex = arguments.findIndex((x) => Boolean(x.match(/^\s*template\s*=.*$/)));
  arguments[templateIndex] = 'template=ListingFields/with custom links';

  const firstFieldIndex = arguments.findIndex((x) => Boolean(x.match(/^\s*\?/)))
  arguments.splice(firstFieldIndex, 0, '?Link for lists');

  return '{{#ask: ' + arguments.join('\n| ') + '\n}}';
}

function preprocessText(s, englishPageName) {
  const replacements = [
    [/\{\{(newin|oldin|deprecatedin|param)\|/g, '{{$1 (Українська)|'],
    [/(=+\s*)Constructor(\s*=+)/g, '$1Конструктор$2'],
    [/(=+\s*)Constructors(\s*=+)/g, '$1Конструктори$2'],
    [/(=+\s*)Function(\s*=+)/g, '$1Функція$2'],
    [/(=+\s*)Functions(\s*=+)/g, '$1Функції$2'],
    [/(=+\s*)Synopsis(\s*=+)/g, '$1Вигляд$2'],
    [/(=+\s*)Argument(\s*=+)/g, '$1Аргумент$2'],
    [/(=+\s*)Arguments(\s*=+)/g, '$1Аргументи$2'],
    [/(=+\s*)Returns(\s*=+)/g, '$1Повертає$2'],
    [/(=+\s*)Examples(\s*=+)/g, '$1Приклади$2'],
    [/(=+\s*)Example(\s*=+)/g, '$1Приклад$2'],
    [/(=+\s*)See Also(\s*=+)/g, '$1Див. також$2'],
    [/(=+\s*)Supertypes(\s*=+)/g, '$1Базові типи$2'],
    [/(=+\s*)Supertype(\s*=+)/g, '$1Базовий тип$2'],
    [/(=+\s*)Other Languages(\s*=+)/g, '$1Іншими мовами$2'],
    [/(=+\s*)Constants(\s*=+)/g, '$1Константи$2'],
    
    // This might not always be a correct translation, but it's so common and it's correct in 90% cases so it's worth including it here
    [/\nNone.\n/g, '\nВідсутні.\n'],

    // if there is a duplicate (Українська) somewhere, it was probably introduced by our script, it shouldn't happen
    [/\(Українська\) \(Українська\)/g, '(Українська)'],
  ];

  let current = s;
  replacements.forEach(([regexToReplace, replacementString]) => {
    current = current.replaceAll(regexToReplace, replacementString);
  });

  if (englishPageName && !current.match(/#set:Link for lists/)) {
    current = current.trim() + "\n{{#set:Link for lists=[[" + englishPageName + " (Українська)|" + englishPageName + "]]}}";
  }

  // we don't process pages with | in them, they need to be handled manually
  current = current.replaceAll(/\[\[\s*([^\]|]+?)\s*\]\]/g, preprocessLink);

  current = current.replaceAll(/\{\{#ask:(.*)}}/gs, preprocessAskCall);

  // TODO: handle lists
  return current;
}

async function preparePageForTranslation(event) {
  const textbox = document.getElementById('wpTextbox1');
  if (textbox) {
    let value = textbox.value;
    const englishPageName = getEnglishPageName();

    if (englishPageName) {
      if (value.trim() === '') {
        value = await getPageWikitext(englishPageName);
      }

      const newValue = preprocessText(value, englishPageName);
      textbox.value = newValue;
    }
  }
}

function addButton() {
  const toolbar = document.getElementById('toolbar');
  if (toolbar) {
    const button = document.createElement('button');
    button.innerText = 'Підготувати переклад';
    button.addEventListener('click', (event) => {
      event.preventDefault();
      preparePageForTranslation();
    });
    toolbar.append(button);
  }
}

addButton();