MediaWiki:Gadget-link-ts.js

注意:保存之后,你必须清除浏览器缓存才能看到做出的更改。Google ChromeFirefoxMicrosoft EdgeSafari:按住⇧ Shift键并单击工具栏的“刷新”按钮。参阅Help:绕过浏览器缓存以获取更多帮助。
/*
 * 取自 oldid=45922506
 * @name Link Translator
 *
 * 操作帮助文档:https://zh.wikipedia.org/wiki/User:和平奮鬥救地球/link-ts
 *
 * 由才女[[User:Liangent]]最初设计
 * 由[[User:Kovl]]修改 v2015-2-6-7-52
 * 由[[User:Panintelize]] 再修改 v2016-02-06
 * 由[[User:和平奮鬥救地球]] 撰写帮助文档 v2016-06-08
 *
 * Logs:
 * 又被[[User:逆襲的天邪鬼]]重新改造 v2016-11-31
 * 用兒童不宜的方式支援了VisualEditor的原始碼2017模式 v2017-02-08
 *
 * 依存程式:[[User:逆襲的天邪鬼/js/EditorAPIs.js]]
 *
 * 参见:
 * * [[User:Artoria2e5/link-ts.js]]
 * * [[en:User:逆襲的天邪鬼/link-ts.js]]
 */

// <pre>

mw.loader.using(['jquery.ui']).then(function () {
    'use strict';

    var LTUI;
    switch (mw.config.get('wgUserLanguage')) {
        case 'zh':
        case 'zh-cn':
        case 'zh-hans':
        case 'zh-my':
        case 'zh-sg':
            LTUI = {
                Translate:              '翻译',
                TranslateLinks:         '翻译链接',
                LinkTranslator:         '链接翻译器',
                TLTitle:                '自动翻译来自语言维基百科文本中的链接',
                SourceLanguageCode:     '目标维基的语言代码:',
                OriginalLink:           '原链接:',
                NOLINKINPAGE:           '此页没有链接',
                Done:                   '完成',
                NoVE:                   'link-ts不支持可视化模式,请切换到源代码模式。',
                EditMessage:            '[[MediaWiki:Gadget-link-ts.js|翻译]]外文链接',

                // OPTION
                SelectedTextOnly:       '只处理选中文本',
                IfFound:                '如果找到对应中文条目的话——',
                KeepOriginalText2:      '以原文显示蓝链([[中文|Original Text]])',
                CommentOriginalLink:    '注释原链接',
                IfNotFound:             '否则的话——',
                UseLangLink:            '转成跨语言链接(即{{tsl}}或{{link-xx}})',
                UseTsl:                 '使用{{tsl}}而非{{link-xx}}',
                KeepOriginalText:       '以原文显示绿链({{tsl|en|Orin||Orin text}})',
                MarkWrongPage:          '如果不转成跨语言链接,而且(在原文或中文)属于红链,那么把它标记出来',

                // STATUS
                PARSEFAILED:            '解析失败',
                ERROR:                  '错误',
                NOLINK:                 '没有链接',
                MULTIPLELINK:           '多个连接',
                PAGESAME:               '页面相同',
                PAGEDIFF:               '页面不同',
                DONTEXIST:              '页面不存在',
            };
            break;
        default:
            LTUI = {
                Translate:              '翻譯',
                TranslateLinks:         '翻譯連結',
                LinkTranslator:         '連結翻譯器',
                TLTitle:                '自動翻譯來自其他語言維基百科文本中的連結',
                SourceLanguageCode:     '目標維基的語言代碼:',
                OriginalLink:           '原連結:',
                NOLINKINPAGE:           '此頁沒有任何連結',
                Done:                   '完成',
                NoVE:                   'link-ts不支援視覺化模式,請切換到原始碼模式。',
                EditMessage:            '[[User:逆襲的天邪鬼/js/link-ts.js|翻譯]]外文連結',

                // OPTION
                SelectedTextOnly:       '只處理選中文本',
                IfFound:                '如果找到對應中文條目的話——',
                KeepOriginalText2:      '以原文顯示藍連([[中文|Original Text]])',
                CommentOriginalLink:    '註釋原連結',
                IfNotFound:             '否則的话——',
                UseLangLink:            '轉成跨語言連結(即{{tsl}}或{{link-xx}})',
                UseTsl:                 '使用{{tsl}}而非{{link-xx}}',
                KeepOriginalText:       '以原文顯示綠連({{tsl|en|Orin||Orin text}})',
                MarkWrongPage:          '如果不轉成跨語言連結,而且(在原文或中文)屬於紅連,那麼把它標記出來',

                // STATUS
                PARSEFAILED:            '解析失敗',
                ERROR:                  '錯誤',
                NOLINK:                 '沒有連結',
                MULTIPLELINK:           '多個連結',
                PAGESAME:               '頁面相同',
                PAGEDIFF:               '頁面不同',
                DONTEXIST:              '頁面不存在',
            };
    }

    var LTConf = {
        SourceLanguageCode:     'en',
        KeepOriginalText:       'checked', // 'checked' OR ''
        KeepOriginalText2:      'checked',
        CommentOriginalLink:    '',
        UseLangLink:            'checked',
        UseTsl:                 'checked',
        SelectedTextOnly:       'checked',
        MarkWrongPage:          'checked',
    };


    // variables
    var currentJobid = 0;
    var LDSB = '__LEFT_DOUBLE_SQUARE_BRACKETS__';
    var EXEConf;

    /**
     * 调用维基百科API来转换条目标题
     */
    var callApi = function (options) {
        var links = options.links || [];
        var callback = options.callback || (function () {});
        var failure = options.failure || (function () {});
        var lang = options.lang || 'en';
        var tolang = options.tolang || mw.config.get('wgContentLanguage');
        var jobid = options.jobid || currentJobid;

        // 标题去重,同时将这些标题分组,因为API只允许一次性最多提交50个标题
        var map = {};
        var i;
        for (i=0; i<links.length; i++) {
            map[links[i]] = true;
        }

        var queue = [];
        var queue2 = [];
        var m = 0, n = 0;
        for (var j in map) {
            queue.push(j);
            m++;
            if (m === 50) {
                m = 0;
                queue2.push(queue);
                queue = [];
                n++;
            }
        }
        if (queue.length > 0) {
            queue2.push(queue);
            n++;
        }

        // 开始处理
        var stop = false;
        var langlinks = {};

        var done = function (data) {
            if (data.query) {
                // 獲取原來的標題
                var orinTitle = {};
                var convert = function (arr) {
                    if (arr) {
                        for (var i=0; i<arr.length; i++) {
                            if (orinTitle[arr[i].from]) {
                                orinTitle[arr[i].to] = orinTitle[arr[i].from];
                            } else {
                                orinTitle[arr[i].to] = arr[i].from;
                            }
                        }
                    }
                };
                convert(data.query.normalized);
                convert(data.query.redirects);

                // 標明翻譯情況
                var pages = data.query.pages;
                for (var pageid in pages) {
                    var page = pages[pageid];
                    var title = page.title;
                    if (orinTitle[title]) {
                        title = orinTitle[title];
                    }

                    if (page.missing !== undefined) {
                        // 页面不存在
                        langlinks[title] = undefined;
                    } else if (page.langlinks) {
                        langlinks[title] = page.langlinks[0]['*'];
                    } else {
                        // 没有对应条目
                        langlinks[title] = null;
                    }
                }
            }

            n--;
            if (n === 0 && !stop) {
                callback(langlinks);
            }
        };

        var error = function (jqXHR, textStatus, errorThrown) {
            stop = true;
            if (jobid === currentJobid) {
                failure(langlinks);
            }
        };

        var process = function (links) {
            if (jobid !== currentJobid) {
                stop = true;
                return;
            }

            $.ajax({
                data: {
                    action: 'query',
                    prop: 'langlinks',
                    lllang: tolang,
                    format: 'json',
                    redirects: true,
                    lllimit: 50,
                    titles: links.join('|'),
                },
                dataType: 'jsonp',
                type: 'POST',
                url: 'https://' + lang + '.wikipedia.org/w/api.php',
                success: done,
                error: error,
            });
        };

        for (i=0; i<queue2.length && !stop; i++) {
            process(queue2[i]);
        }
    };

    /**
     * 根据要求,将跨语言链接变成普通链接、{{tsl}}或{{link}}。Category会被特殊处理。
     */
    var makeLink = function (options) {
        var link = options.link || { target: '', display: '' };
        var lang = options.lang || '';
        var newTarget = options.newTarget;
        var output = '';

        if (!link) {
            return '';
        }

        var makeNormalLink = function (target, display) {
            if (target === display || display === '') {
                return LDSB + target + ']]';
            } else {
                return LDSB + target + '|' + display + ']]';
            }
        };

        // 禁止把页面分类变成跨语言链接,或者在其后面附加文字(除非是[Category:XXX|*]那种)
        if (link.target.toLowerCase().indexOf('category:') === 0) {
            options.useLangLink = false;
            options.keepOriginalText2 = (link.display !== '' && link.display !== link.target);
        }

        if (newTarget === null) {
            // 没有对应中文条目
            if (options.useLangLink) {
                if (options.useTsl) {
                    if (options.keepOriginalText) {
                        return '{{tsl|' + lang + '|' + link.target + '||' + link.display + '}}';
                    } else {
                        return '{{tsl|' + lang + '|' + link.target + '|}}';
                    }
                } else {
                    if (options.keepOriginalText) {
                        return '{{link-' + lang + '||' + link.target + '|' + link.display + '}}';
                    } else {
                        return '{{link-' + lang + '||' + link.target + '}}';
                    }
                }
            } else {
                if (options.markWrongPage) {
                    return '<!-- ' + LTUI.NOLINK + ' -->' + makeNormalLink(link.target, link.display);
                } else {
                    return makeNormalLink(link.target, link.display);
                }
            }
        } else if (newTarget === undefined) {
            // 页面不存在
            if (options.markWrongPage) {
                return '<!-- ' + LTUI.DONTEXIST + ' -->' + makeNormalLink(link.target, link.display);
            } else {
                return makeNormalLink(link.target, link.display);
            }
        } else {
            if (options.keepOriginalText2) {
                output = makeNormalLink(newTarget, link.display || link.target);
            } else {
                output = makeNormalLink(newTarget, '');
            }
        }

        if (options.commentOriginalLink) {
            output = '<!-- ' + makeNormalLink(link.target, link.display) + ' -->' + output;
        }

        return output;
    };

    /**
     * 开始进行处理
     */
    var processLinks = function (event) {
        var jobid = currentJobid;

        EXEConf = {
            KeepOriginalText:       $('#linktranslator-keep-original').prop('checked'),
            KeepOriginalText2:      $('#linktranslator-keep-original2').prop('checked'),
            CommentOriginalLink:    $('#linktranslator-comment-link').prop('checked'),
            UseLangLink:            $('#linktranslator-lang-link').prop('checked'),
            UseTsl:                 $('#linktranslator-tsl').prop('checked'),
            SelectedTextOnly:       $('#linktranslator-selected-text').prop('checked'),
            MarkWrongPage:          $('#linktranslator-markwrongpage').prop('checked'),
        };

        LTConf.SourceLanguageCode = $('#linktranslator-source-lang').val();

        var wikitext = wikitextEditor.text;
        if (EXEConf.SelectedTextOnly) {
            wikitext = wikitextEditor.selectionText;
        }

        var links = wikitext.match(/(\[\[)(?!\:?.?.?\:)(?!Image:)(?!File:)(.+?)(\|.+?)?(\]\])/g);

        if (links === null) {
            $('#linktranslator').text(LTUI.NOLINKINPAGE);
            return;
        } else {
            $('#linktranslator').dialog( 'option', 'position', { my: 'top', at: 'top'} );
            $('#linktranslator').html('<div id="linktranslator-progressbar"></div>');
            $('#linktranslator-progressbar').progressbar();
        }

        // 整合各links
        var realLinks = [];
        var links2 = [];
        for (var i=0; i<links.length; i++) {
            var link = links[i].slice(2, -2);

            var linktarget = link;
            var linkdisplay = link;

            var idx = link.indexOf('|');
            if (idx !== -1) {
                linktarget = link.substring(0, idx);
                linkdisplay = link.substring(idx + 1);
            }
            // 通常情況下外文維基的錨點到中文維基就沒用了
            idx = linktarget.indexOf('#');
            if (idx !== -1) {
                linktarget = link.substring(0, idx);
            }
            $('#linktranslator').append('<div id="linktranslator-item-' + i + '"></div>');
            $('#linktranslator-item-' + i).text(links[i] + ' -> ')
                .append('<span class="linktranslator-item-newlink">...</span>');

            realLinks.push(linktarget);
            links2.push({ target: linktarget, display: linkdisplay });
        }

        var apiCallback = function (langlinks) {
            var code = wikitext + '';

            for (var i=0; i<links2.length; i++) {
                var link = links2[i];

                // 处理文内链接
                var linkCode = makeLink({
                    link: link,
                    newTarget: langlinks[link.target],
                    lang: LTConf.SourceLanguageCode,
                    useLangLink: EXEConf.UseLangLink,
                    useTsl: EXEConf.UseTsl,
                    keepOriginalText: EXEConf.KeepOriginalText,
                    keepOriginalText2: EXEConf.KeepOriginalText2,
                    commentOriginalLink: EXEConf.CommentOriginalLink,
                    markWrongPage: EXEConf.MarkWrongPage,
                });

                //mark on dialogue
                $('#linktranslator-item-' + i + ' .linktranslator-item-newlink').text(linkCode.replace(new RegExp(LDSB, 'g'), '[[')).css('color', '#0645ad');

                if (langlinks[link.target] === null) {
                    // 没有对应中文条目
                    $('#linktranslator-item-' + i + ' .linktranslator-item-newlink').addClass('ilh-tool');
                    if (!EXEConf.UseLangLink) {
                        $('#linktranslator-item-' + i + ' .linktranslator-item-newlink').text(LTUI.NOLINK);
                    }
                } else if (langlinks[link.target] === undefined) {
                    // 页面不存在
                    $('#linktranslator-item-' + i + ' .linktranslator-item-newlink').text(LTUI.DONTEXIST).css('color', '#ba0000');
                } else if (langlinks[link.target] === link) {
                    $('#linktranslator-item-' + i + ' .linktranslator-item-newlink').text(LTUI.PAGESAME);
                }

                // only replacing the first is ok, we will run this many times
                code = code.replace(links[i], linkCode);
            }

            return code.replace(new RegExp(LDSB, 'g'), '[[');
        };

        // 开始处理
        // 过去是一个条目调用一次API,现在是一次性处理全部,所以滚动条已经没有意义。
        // var respcount = 0;
        callApi({
            links: realLinks,
            lang: LTConf.SourceLanguageCode,
            jobid: jobid,
            callback: function (langlinks) {
                if (jobid !== currentJobid) {
                    return;
                }

                var newtext = apiCallback(langlinks);

                //$('#linktranslator-progressbar').progressbar('value', respcount * 100 / links.length);
                $('#linktranslator-progressbar').progressbar('value', 100);
                $('#linktranslator').prepend('<div id="linktranlator-done"><strong>' + LTUI.Done + '</strong></div>');

                if (EXEConf.SelectedTextOnly) {
                    wikitextEditor.selectionText = newtext;
                } else {
                    wikitextEditor.text = newtext;
                }
                if (!wikitextEditor.summary) {
                    wikitextEditor.summary = LTUI.EditMessage;
                }

                // 針對VE進行Hack
                wikitextEditor.keepSelection = false;
            },
            failure: function (langlinks) {
                $('#linktranslator').prepend('<div id="linktranlator-done" style="color:red;"><strong>' + LTUI.ERROR + '</strong></div>');
            },
        });
    };

    // clear previous button
    $('#wpLinktranslator').remove();

    var callDialog = function (event) {
        if (event && event.preventDefault) {
            event.preventDefault();
        }
        $('#linktranslator').remove();

        if (wikitextEditor.mode === 'visual') {
            alert(LTUI.NoVE);
            return;
        }

        // 針對VE進行Hack
        wikitextEditor.keepSelection = true;

        if (wikitextEditor.selectionText.length > 0) {
            LTConf.SelectedTextOnly = 'checked';
        } else {
            LTConf.SelectedTextOnly = '';
        }

        $('<div id="linktranslator" title="' + LTUI.LinkTranslator + '">' +
            '<label for="linktranslator-source-lang">' + LTUI.SourceLanguageCode + '</label> ' +
            '<input id="linktranslator-source-lang" value="' + LTConf.SourceLanguageCode + '" type="text" /><br><br>' +
            '<input type="checkbox" id="linktranslator-selected-text" ' + LTConf.SelectedTextOnly + '/> ' +
            '<label for="linktranslator-selected-text">' + LTUI.SelectedTextOnly + '</label><br>' +
            '<br>' + LTUI.IfFound + '<br><br>' +
            '<input type="checkbox" id="linktranslator-comment-link" ' + LTConf.CommentOriginalLink + '/> ' +
            '<label for="linktranslator-comment-link">' + LTUI.CommentOriginalLink + '</label><br>' +
            '<input type="checkbox" id="linktranslator-keep-original2" ' + LTConf.KeepOriginalText2 + '/> ' +
            '<label for="linktranslator-keep-original2">' + LTUI.KeepOriginalText2 + '</label><br>' +
            '<br>' + LTUI.IfNotFound + '<br><br>' +
            '<input type="checkbox" id="linktranslator-lang-link" ' + LTConf.UseLangLink + '/> ' +
            '<label for="linktranslator-lang-link">' + LTUI.UseLangLink + '</label><br>' +
            '<input type="checkbox" id="linktranslator-tsl" ' + LTConf.UseTsl + '/> ' +
            '<label for="linktranslator-tsl">' + LTUI.UseTsl + '</label><br>' +
            '<input type="checkbox" id="linktranslator-keep-original" ' + LTConf.KeepOriginalText + '/> ' +
            '<label for="linktranslator-keep-original">' + LTUI.KeepOriginalText + '</label><br>' +
            '<input type="checkbox" id="linktranslator-markwrongpage" ' + LTConf.MarkWrongPage + '/> ' +
            '<label for="linktranslator-markwrongpage">' + LTUI.MarkWrongPage + '</label><br>'
        ).dialog({
            modal: false,
            close: function() {
                currentJobid++;
            },
            width: 500,
            buttons: [
                {
                    text: LTUI.Translate,
                    click: function () {
                        $('#linktranslator').dialog('option', 'buttons', []);
                        processLinks();
                    }
                }
            ]
        });
    };


    // 設法支援各路編輯器
    mw.hook('editorapi.ready').add(function () {
        if (['wikitext', 'wikEd', 'codemirror'].indexOf(wikitextEditor.mode) !== -1) {
            $('#wpLinktranslator').remove();
            $('#wpLinktranslatorFUCK').remove();
            var FUCKYOU = $('#wpDiffWidget').length > 0;
            if (FUCKYOU) {
                $('#wpDiffWidget').after('<span id="wpLinktranslatorFUCK" aria-disabled="false" class="oo-ui-widget oo-ui-widget-enabled oo-ui-inputWidget oo-ui-buttonElement oo-ui-buttonElement-framed oo-ui-labelElement oo-ui-buttonInputWidget"><input id="wpLinktranslator" value="' + LTUI.TranslateLinks + '" title="' + LTUI.TLTitle + '" type="button" class="oo-ui-inputWidget-input oo-ui-buttonElement-button"/></span>');
            } else {
                $('#wpDiff').after('\n<input id="wpLinktranslator" value="' + LTUI.TranslateLinks + '" title="' + LTUI.TLTitle + '" type="button"/>');
            }
            $('#wpLinktranslator').click(callDialog);
        }
        $('#p-link-ts').remove();
        $(mw.util.addPortletLink('p-cactions', '#', LTUI.TranslateLinks, 'p-link-ts')).click(callDialog);

        currentJobid++;
        try {
            $('#linktranslator').dialog('close');
        } catch (ex) {

        }
    });
    mw.loader.load('https://zh.wikipedia.org/w/index.php?title=MediaWiki:Gadget-EditorAPIs.js&action=raw&ctype=text/javascript');
});

// </pre>
// [[Category:维基脚本]]