Google Translate(翻訳)コマンドラインツール

Google Chromeにはhttps://chrome.google.com/extensions/detail/jlhlebbhengjlhmcjebbkambaekglhkfを入れ、FirefoxThunderbirdにはhttps://addons.mozilla.org/en-US/firefox/addon/58606/を入れて、英語の長文はさっさとGoogle Translateに投げて、大体の文脈を掴むことにしている。一昔前よりは遥かに「読める」日本語文章が返ってくるから、本当に便利になったと思う。
しかし、weechatなど、ターミナル上のソフトウェアによって表示される英文に対して、Google Translateへの適切なショートカットが無かった。
そこで、標準入力から入力した文章をGoogle Translateを通し、翻訳された文章を標準出力に出力するツールを作ることにした。ほとんどは英語→日本語で用いるだろうが、オプションで入力言語と出力言語を指定出来ればなお良いと考えた。
Java(あるいはScalaClojureなど)だとGoogle Code Archive - Long-term storage for Google Code Project Hosting.があるし、Pythonならurllibとurllib2とjsonを使えば20数行で書ける。


ちょっと頭の体操がてら、Haskellで書いてみた。
HTTP通信のためにhttp packageJSON処理のためにjson packageをcabalでインストールした。他のライブラリはMacPortsからインストールしたHaskell Platformに標準搭載されている。

まず使用例。

$ ghc --make translator.hs
$ ./translator -fja -ten  # -fまたは--from=で入力言語を指定, -tまたは--to=で出力言語を指定
クリエイティブ・コモンズとは、クリエイティブ・コモンズ・ライセンス(CCライセンス)を提供している国際的非営利組織とそのプロジェクトの総称です。
# <- C-dを押す
Creative Commons license Creative Commons (CC license) is a general term for an international nonprofit organization and the project has been provided.

$ ./translator  # オプションを省略した場合は-fen -tja
Creative Commons is a nonprofit corporation dedicated to making it easier for people to share and build upon the work of others, consistent with the rules of copyright. 
クリエイティブコモンズは、簡単に人々が共有し、他の作品、著作権のルールと一致する時にビルドすることに専念非営利法人です。

$ cat document.txt | ./translator
# (出力省略)
$ ./translator < document.txt
# (出力省略)

$ ./translator -fes -tja  # Google Translate対応言語は勿論対応
Alguna de estas condiciones puede no aplicarse si se obtiene el permiso del titular de los derechos de autor
あなたが著作権の所有者から許可を得ると、これらの条件は免除することができます

コードを以下に掲載する。ちょっと長いので、A Command-line Tool for Google Translate · GitHubにも掲載した。

-- A Command-line Tool for Google Translate
-- Copyright : (c) SAEKI Yoshiyasu
-- License   : MIT-style license <http://www.opensource.org/licenses/mit-license.php>
-- last updated: 2010/10/16

import Network.URI
import Data.List
import Network.HTTP
import Text.JSON
import System
import System.Console.GetOpt
import Data.Maybe (fromMaybe)

urlencode :: String -> String
urlencode s = escapeURIString isAlphaNum s
              where
                isAlphaNum c = elem c (['0'..'9'] ++ ['A'..'Z'] ++ ['a'..'z'])

mkParams :: [(String, String)] -> String
mkParams sss = (intercalate "&") $ map pair sss
               where
                 pair e = (urlencode $ fst e) ++ "=" ++ (urlencode $ snd e)

type URL = String

mkGtUrl :: Options -> String -> URL
mkGtUrl optlist text = baseurl ++ (mkParams params)
            where
              baseurl = "http://ajax.googleapis.com/ajax/services/language/translate?"
              params = [("v", "1.0"), ("langpair", fl ++ "|" ++ tl), ("q", text)]
              (Just fl) = froml optlist
              (Just tl) = tol optlist

getHTTPContent :: URL -> IO String
getHTTPContent url = (simpleHTTP $ insertHeader HdrReferer myUrl $ getRequest url)
                     >>= getResponseBody
                         where myUrl = "http://d.hatena.ne.jp/LaclefYoshi/"

toJV :: String -> JSValue
toJV content = let Ok value = decodeStrict content
               in value

getTranslatedtext :: JSValue -> String
getTranslatedtext (JSObject v) = let Ok (JSObject rd) = valFromObj "responseData" v
                                 in let Ok tt = valFromObj "translatedText" rd
                                    in fromJSString tt

data Options = Options {froml :: Maybe String,
                        tol   :: Maybe String} deriving Show

defaultOptions :: Options
defaultOptions = Options {froml  = Just "en", tol = Just "ja"}

options :: [OptDescr (Options -> Options)]
options =
    [ Option ['f'] ["from"]
        (OptArg ((\f opts -> opts { froml = Just f }) . fromMaybe "en") "lang") "FROM",
      Option ['t'] ["to"]
        (OptArg ((\t opts -> opts { tol = Just t }) . fromMaybe "ja") "lang") "TO" ]

compilerOpts :: [String] -> IO (Options, [String])
compilerOpts argv =
    case getOpt Permute options argv of
      (o,n,[]  ) -> return (foldl (flip id) defaultOptions o, n)
      (_,_,errs) -> ioError (userError (concat errs ++ usageInfo header options))
        where header = "Usage: PROGRAM [OPTION...] files..."

main :: IO ()
main = do
  argv <- getArgs
  (opts, optvalues) <- compilerOpts argv
  text <- getContents
  content <- getHTTPContent $ mkGtUrl opts text
  putStrLn $ getTranslatedtext $ toJV content

getOptの使い方はライブラリドキュメントのコピペ。"Real World Haskell"だと28.8.4に解説がある。
リファラの設定は、Cloud Translation API documentation  |  Cloud Translation API  |  Google Cloudで必ず設定しろとあるので、このブログのURLを設定してある。
相変わらず、Haskellコーディングは手探り状態だ。