translate 2009.12.17 → 2010.1.24
raw patch · 7 files changed
+242/−98 lines, 7 filesdep +utf8-stringPVP ok
version bump matches the API change (PVP)
Dependencies added: utf8-string
API changes (from Hackage documentation)
- Text.Translate: instance Data RBad
- Text.Translate: instance Data RGood
- Text.Translate: instance Data RStatus
- Text.Translate: instance Data RText
- Text.Translate: instance Eq RBad
- Text.Translate: instance Eq RGood
- Text.Translate: instance Eq RStatus
- Text.Translate: instance Eq RText
- Text.Translate: instance Show RBad
- Text.Translate: instance Show RGood
- Text.Translate: instance Show RStatus
- Text.Translate: instance Show RText
- Text.Translate: instance Typeable RBad
- Text.Translate: instance Typeable RGood
- Text.Translate: instance Typeable RStatus
- Text.Translate: instance Typeable RText
+ Text.Language.Detect: detect :: String -> IO (Maybe (String, Bool, Double))
+ Text.Language.Detect: detectCode :: String -> IO (Maybe String)
+ Text.Language.Detect: instance Data RGood
+ Text.Language.Detect: instance Data RText
+ Text.Language.Detect: instance Eq RGood
+ Text.Language.Detect: instance Eq RText
+ Text.Language.Detect: instance Show RGood
+ Text.Language.Detect: instance Show RText
+ Text.Language.Detect: instance Typeable RGood
+ Text.Language.Detect: instance Typeable RText
+ Text.Language.Internals: (-) :: (a -> b) -> a -> b
+ Text.Language.Internals: (.) :: a -> (a -> b) -> b
+ Text.Language.Internals: RBad :: String -> RBad
+ Text.Language.Internals: RStatus :: Integer -> RStatus
+ Text.Language.Internals: curl :: String -> IO (Maybe String)
+ Text.Language.Internals: data RBad
+ Text.Language.Internals: data RStatus
+ Text.Language.Internals: escapeURIChar :: (Char -> Bool) -> Char -> String
+ Text.Language.Internals: escapeURIString :: (Char -> Bool) -> String -> String
+ Text.Language.Internals: escape_uri :: String -> String
+ Text.Language.Internals: google_api :: String -> [(String, String)] -> String
+ Text.Language.Internals: instance Data RBad
+ Text.Language.Internals: instance Data RStatus
+ Text.Language.Internals: instance Eq RBad
+ Text.Language.Internals: instance Eq RStatus
+ Text.Language.Internals: instance Show RBad
+ Text.Language.Internals: instance Show RStatus
+ Text.Language.Internals: instance Typeable RBad
+ Text.Language.Internals: instance Typeable RStatus
+ Text.Language.Internals: join :: [a] -> [[a]] -> [a]
+ Text.Language.Internals: responseDetails :: RBad -> String
+ Text.Language.Internals: responseStatus :: RStatus -> Integer
+ Text.Language.Translate: instance Data RGood
+ Text.Language.Translate: instance Data RText
+ Text.Language.Translate: instance Eq RGood
+ Text.Language.Translate: instance Eq RText
+ Text.Language.Translate: instance Show RGood
+ Text.Language.Translate: instance Show RText
+ Text.Language.Translate: instance Typeable RGood
+ Text.Language.Translate: instance Typeable RText
+ Text.Language.Translate: translate :: String -> String -> String -> IO (Maybe String)
Files
- changelog.md +8/−0
- readme.md +5/−2
- src/Text/Language/Detect.hs +75/−0
- src/Text/Language/Internals.hs +90/−0
- src/Text/Language/Translate.hs +51/−0
- src/Text/Translate.hs +3/−88
- translate.cabal +10/−8
changelog.md view
@@ -0,0 +1,8 @@+# 2010.01.24 Joao F. Ferreira <joao@joaoff.com>++ - Added support for language detection: `Text.Language.Detect`+ - `Text.Translate` is now `Text.Language.Translate`, but I've maintained+ `Text.Translate` for backward compatibility+ - Fixed bug with URI escaping. Google's API is expecting an ASCII string+ that is first escaped with UTF-8.+ - Refactored parts of the module to avoid repeated code
readme.md view
@@ -1,15 +1,18 @@ Translate ========== -Haskell binding to Google translate+Haskell binding to Google's AJAX Language API for Translation and Detection ghci - > :m Text.Translate+ > :m Text.Language.Translate > translate "en" "fr" "hello" Just "bonjour" + > :m Text.Language.Detect+ > detect "Program testing can be used to show the presence of bugs, but never to show their absence!" + Just ("en",True,0.87355334)
+ src/Text/Language/Detect.hs view
@@ -0,0 +1,75 @@+{-# LANGUAGE DeriveDataTypeable #-}++module Text.Language.Detect (detect,detectCode) where++import Text.Language.Internals+import Text.JSON.Generic +import Prelude hiding ((.), (-))++-- http://ajax.googleapis.com/ajax/services/language/detect?v=1.0&q=Hello+World++{-+{"responseData"=>{"language"=>"en","isReliable"=>false,"confidence"=>0.114892714},+ "responseStatus"=>200,+ "responseDetails"=>nil}+-}++-- Datatypes+data RText = RText+ {+ language :: String,+ isReliable :: Bool,+ confidence :: Double+ }+ deriving (Eq, Show, Data, Typeable)++data RGood = RGood+ {+ responseData :: RText+ }+ deriving (Eq, Show, Data, Typeable)+ ++base_url :: String+base_url = "http://ajax.googleapis.com/ajax/services/language/detect"++detect_api :: String -> String+detect_api what = google_api base_url [("v", "1.0"), ("q", what)] + +-- | Returns the language code associated with the given text+detectCode :: String -> IO (Maybe String)+detectCode what = do+ r <- curl - detect_api what+ case r of+ Nothing -> return - Nothing+ Just x -> + let status = x.decodeJSON+ in+ if status.responseStatus == 200+ then do+ let rgood = x.decodeJSON+ return - Just - rgood.responseData.language+ else do+ return Nothing++-- | Returns a triple where the first component is the language code associated with+-- given text, the second is a boolean representing whether or not the detection interval +-- believes the language code is reliable for the given text, and the third is a+-- numeric value between 0-1.0 that represents the confidence level in the language code+-- for the given text.+detect :: String -> IO (Maybe (String,Bool,Double))+detect what = do+ r <- curl - detect_api what+ case r of+ Nothing -> return - Nothing+ Just x -> + let status = x.decodeJSON+ in+ if status.responseStatus == 200+ then do+ let rgood = x.decodeJSON+ return - Just - (rgood.responseData.language,+ rgood.responseData.isReliable,+ rgood.responseData.confidence)+ else do+ return Nothing
+ src/Text/Language/Internals.hs view
@@ -0,0 +1,90 @@+{-# LANGUAGE DeriveDataTypeable #-}++module Text.Language.Internals where++import Text.JSON.Generic+import Network.Curl+import qualified Data.List as L+import Prelude hiding ((.), (-))+import Network.URI (isAllowedInURI)+import qualified Codec.Binary.UTF8.String as Utf+import Numeric++-- This module factors out auxiliar and similar functions to the Text.Language modules++-- Datatypes+data RStatus = RStatus+ {+ responseStatus :: Integer+ }+ deriving (Eq, Show, Data, Typeable)++ +data RBad = RBad+ {+ responseDetails :: String+ }+ deriving (Eq, Show, Data, Typeable)+++-- | Perform a request using Network.Curl+--+curl :: String -> IO (Maybe String)+curl x = do+ (r, s) <- curlGetString x []+ if r == CurlOK+ then return - Just s+ else return Nothing++-- | Constructs a string with the given arguments ready to be sent+-- to Google's APIs+google_api :: String -> [(String,String)] -> String+google_api base_url args = + let make_pair (x, y) = x ++ "=" ++ escape_uri y+ in+ base_url ++ "?" ++ args .map make_pair .join "&"+ +++-- bolerplate++-- base DSL+{-# INLINE (.) #-}+(.) :: a -> (a -> b) -> b+a . f = f a+infixl 9 .++{-# INLINE (-) #-}+(-) :: (a -> b) -> a -> b+f - x = f x+infixr 0 - ++join :: [a] -> [[a]] -> [a]+join = L.intercalate++++-- Google APIs encode text as ASCII, but it is first escaped with UTF-8.+-- Therefore, the |escapeURIString| function in Network.URI cannot be used. +-- We redefine it here.++-- | Can be used to validate the URI sent to Google's API+--+escape_uri :: String -> String+escape_uri = escapeURIString isAllowedInURI++-- | Escapes a special character with UTF-8.+--+escapeURIChar :: (Char->Bool) -> Char -> String+escapeURIChar p c+ | p c = [c]+ | otherwise = concatMap ('%':) $ map (flip showHex "") $ Utf.encode [c]++-- | Can be used to make a string valid for use in a URI.+--+escapeURIString+ :: (Char->Bool) -- ^ a predicate which returns 'False'+ -- if the character should be escaped+ -> String -- ^ the string to process+ -> String -- ^ the resulting URI string+escapeURIString p s = concatMap (escapeURIChar p) s
+ src/Text/Language/Translate.hs view
@@ -0,0 +1,51 @@+{-# LANGUAGE DeriveDataTypeable #-}++module Text.Language.Translate (translate) where++import Text.Language.Internals++import Text.JSON.Generic+import Prelude hiding ((.), (-))++-- http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&langpair=en|de&q=Hello+World++{-+{"responseData"=>{"translatedText"=>"Hallo Welt"},+ "responseStatus"=>200,+ "responseDetails"=>nil}+-}++-- Datatypes+data RText = RText+ {+ translatedText :: String+ }+ deriving (Eq, Show, Data, Typeable)++data RGood = RGood+ {+ responseData :: RText+ }+ deriving (Eq, Show, Data, Typeable)+ ++base_url :: String+base_url = "http://ajax.googleapis.com/ajax/services/language/translate"++trans_api :: String -> String -> String -> String+trans_api from to what = google_api base_url [("v", "1.0"), ("langpair", from ++ "|" ++ to), ("q", what)]++translate :: String -> String -> String -> IO (Maybe String)+translate from to what = do+ r <- curl - trans_api from to what+ case r of+ Nothing -> return - Nothing+ Just x -> + let status = x.decodeJSON+ in+ if status.responseStatus == 200+ then do+ let rgood = x.decodeJSON+ return - Just - rgood.responseData.translatedText+ else do+ return Nothing
src/Text/Translate.hs view
@@ -2,92 +2,7 @@ module Text.Translate (translate) where -import Text.JSON.Generic-import Network.Curl-import qualified Data.List as L-import Prelude hiding ((.), (-))-import Network.URI---- http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&langpair=en|de&q=Hello+World--{--{"responseData"=>{"translatedText"=>"Hallo Welt"},- "responseStatus"=>200,- "responseDetails"=>nil}--}--curl :: String -> IO (Maybe String)-curl x = do- (r, s) <- curlGetString x []- if r == CurlOK- then return - Just s- else return Nothing--base_url :: String-base_url = "http://ajax.googleapis.com/ajax/services/language/translate"--trans_api :: String -> String -> String -> String-trans_api from to what = - let make_pair (x, y) = x ++ "=" ++ escape_uri y- in- base_url ++ "?" ++ [("v", "1.0"), ("langpair", from ++ "|" ++ to), ("q", what)] .map make_pair .join "&"--data RStatus = RStatus- {- responseStatus :: Integer- }- deriving (Eq, Show, Data, Typeable)--data RText = RText- {- translatedText :: String- }- deriving (Eq, Show, Data, Typeable)--data RGood = RGood- {- responseData :: RText- }- deriving (Eq, Show, Data, Typeable)- -data RBad = RBad- {- responseDetails :: String- }- deriving (Eq, Show, Data, Typeable)--translate :: String -> String -> String -> IO (Maybe String)-translate from to what = do- r <- curl - trans_api from to what- case r of- Nothing -> return - Nothing- Just x -> - let status = x.decodeJSON- in- if status.responseStatus == 200- then do- let rgood = x.decodeJSON- return - Just - rgood.responseData.translatedText- else do- return Nothing----- bolerplate----- base DSL-{-# INLINE (.) #-}-(.) :: a -> (a -> b) -> b-a . f = f a-infixl 9 .--{-# INLINE (-) #-}-(-) :: (a -> b) -> a -> b-f - x = f x-infixr 0 - --join :: [a] -> [[a]] -> [a]-join = L.intercalate+-- This module is just a wrapper to Text.Language.Translate+-- It exists for backward compatibility. -escape_uri :: String -> String-escape_uri = escapeURIString isAllowedInURI+import Text.Language.Translate (translate)
translate.cabal view
@@ -1,24 +1,26 @@ Name: translate-Version: 2009.12.17+Version: 2010.1.24 Build-type: Simple-Synopsis: Haskell binding to Google translate-Description: translate "en" "fr" "hello"-+Synopsis: Haskell binding to Google's AJAX Language API for Translation and Detection+Description: Haskell binding to Google's AJAX Language API for Translation and Detection. License: BSD3 License-file: LICENSE-Author: Wang, Jinjing-Maintainer: Wang, Jinjing <nfjinjing@gmail.com>+Author: Joao F. Ferreira <joao@joaoff.com>, Jinjing Wang <nfjinjing@gmail.com>+Maintainer: Jinjing Wang <nfjinjing@gmail.com> Build-Depends: base Cabal-version: >= 1.2-category: Codec+category: Text homepage: http://github.com/nfjinjing/translate data-files: readme.md, changelog.md library ghc-options: -Wall- build-depends: base >= 4 && < 5, curl, json, network+ build-depends: base >= 4 && < 5, curl, json, network, utf8-string hs-source-dirs: src/ exposed-modules: Text.Translate+ Text.Language.Translate+ Text.Language.Detect+ Text.Language.Internals