【Apple Script】右クリックからGoogle翻訳する

Google翻訳をしたい時に、毎回ブラウザを開いて検索するのが面倒だったので、

翻訳したい英文テキストを選択して、右クリックからすぐにGoogle翻訳できるようにしてみた。

右クリックで使用するのは、以前にも使った「クイックアクション」の機能。

Google翻訳.workflow

(ワークフローが受け取る現在の項目:テキスト)

【Apple Script を実行】


property repeatCount : 20
property delayTime : 0.5

on run argv

    set text1  to ""

    if (count of argv) > 0 then
        set text1 to item 1 of argv as text
    else
        return
    end if

    tell application "Safari"
        activate
        tell window 1

            set home_url to "https://translate.google.co.jp/?hl=ja"
            open location my home_url

            set bounds to {50, 50, 1000, 1000}
            my isLoaded({url_:home_url})

            set text2 to my escape({text_:text1})

            set xpath to my getXPathBySelector({sel_:"textarea[id=\"source\"]"})
            my inputByXPath({xpath_:xpath, val_:text2})

        end tell

    end tell

end run

on isLoaded(args)
    set {url_:aURL} to args & {url_:""}
    tell application "Safari"
        repeat my repeatCount times
            if (URL of document 1 as text) contains aURL then
                repeat my repeatCount times
                    if (do JavaScript "document.readyState" in document 1) is "complete" then
                        return true
                    end if
                    delay my delayTime
                end repeat
                return false
            end if
            delay my delayTime
        end repeat
        return false
    end tell
end isLoaded

on escape(args)
    set {text_:aText} to ¬
        args & {text_:""}

    set text1 to aText
    set text1 to my replace({text_:text1, find_:"'", replace_:"\'"})
    set text1 to my replace({text_:text1, find_:"
", replace_:" "}) --\r\n
    set text1 to my replace({text_:text1, find_:"
", replace_:" "}) --\r
    set text1 to my replace({text_:text1, find_:"
", replace_:" "}) --\n

    return text1
end escape

on replace(args)
    set {text_:aText, find_:aFind, replace_:theReplace} to ¬
        args & {text_:"", find_:"", replace_:""}
    set temp to AppleScript's text item delimiters
    set AppleScript's text item delimiters to aFind
    set theList to every text item of aText
    set AppleScript's text item delimiters to theReplace
    set aText to theList as string
    set AppleScript's text item delimiters to temp
    return aText
end replace

on getXPathBySelector(args)

    set {debug_:aDebug, sel_:aSel, text_:aText, index_:aIndex, xpath_:aXPath, scroll_flg_:aScrollFlg} to ¬
        args & {debug_:false, sel_:"", text_:"", index_:-1, xpath_:"", scroll_flg_:false}

    tell application "Safari"

        set cnt_max to 20
        set cnt_current to 0

        if aScrollFlg then
            do JavaScript "window.scroll(0, 0);" in document 1
        end if

        repeat cnt_max times

            set cnt_current to cnt_current + 1

            if aScrollFlg then

                do JavaScript "

            var element = document.documentElement;
            var bottom = element.scrollHeight - element.clientHeight;
            var scrollY = 0;

            scrollY = (bottom / " & cnt_max & ") * " & cnt_current & ";

            window.scroll(0, scrollY);
            " in document 1

            end if

            if aScrollFlg then
                delay 0.1
            end if

            set val to do JavaScript "
        var val = '';

" & my getTopElement({xpath_:aXPath}) & "

        var lists = ele.querySelectorAll(':scope " & aSel & "');

        for ( var i = 0; i < lists.length; i++ ) {
            var ele = lists[i];

            if((" & aIndex & " == -1) && ('" & aText & "' == '')){
                val = getXpath(ele);
                break;
            }

            if(" & aIndex & " > -1){
                if(" & aIndex & " == i){
                    val = getXpath(ele);
                    break;
                }
            }

            if('" & aText & "' != ''){
                if(ele.innerHTML.indexOf( '" & aText & "') > -1){
                    val = getXpath(ele);
                    break;
                }
            }
        }

" & my getJsFuncXPath() & "

        res = val;
        " in document 1

            if val is not "" then
                return val
            end if

            delay 0.1
        end repeat
        return ""
    end tell
end getXPathBySelector

on inputByXPath(args)
    set {val_:aVal, xpath_:aXPath} to ¬
        args & {val_:"", xpath_:""}
    tell application "Safari"
        do JavaScript "
" & my getTopElement({xpath_:aXPath}) & "
                    ele.value = '" & aVal & "';
        " in document 1
    end tell
end inputByXPath

on getTopElement(args)

    set {xpath_:aXPath} to args & {xpath_:""}

    if aXPath is "" then
        set top_element to "var ele = document;"
    else
        set top_element to "
            var xpaths = document.evaluate('" & aXPath & "', document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null );
            var ele = xpaths.snapshotItem(0);"
    end if

    return top_element

end getTopElement

on getJsFuncXPath()

    return "

function getXpath(element) {
  if(element && element.parentNode) {
    var xpath = getXpath(element.parentNode) + '/' + element.tagName;
    var s = [];

    for(var i = 0; i < element.parentNode.childNodes.length; i++) {
      var e = element.parentNode.childNodes[i];
      if(e.tagName == element.tagName) {
        s.push(e);
      }
    }

    if(1 < s.length) {
      for(var i = 0; i < s.length; i++) {
        if(s[i] === element) {
          xpath += '[' + (i+1) + ']';
          break;
        }
      }
    }

    return xpath.toLowerCase();
  } else {
    return '';
  }
}
    "

end getJsFuncXPath

ブラウザの拡張機能でも翻訳が付けられますが、今回のはテキストならメモ帳など、どこからでも使えるということで。

参考サイト

システム環境設定 > キーボード > ショートカット