microsoft-translator (empty) → 0.1.0.0
raw patch · 10 files changed
+739/−0 lines, 10 filesdep +basedep +bytestringdep +http-api-datasetup-changed
Dependencies added: base, bytestring, http-api-data, http-client, http-client-tls, http-media, mtl, safe, servant, servant-client, text, time, xml
Files
- ChangeLog.md +5/−0
- LICENSE +20/−0
- Setup.hs +2/−0
- microsoft-translator.cabal +46/−0
- src/Microsoft/Translator.hs +253/−0
- src/Microsoft/Translator/API.hs +173/−0
- src/Microsoft/Translator/API/Auth.hs +77/−0
- src/Microsoft/Translator/Exception.hs +21/−0
- src/Microsoft/Translator/Language.hs +122/−0
- src/Microsoft/TranslatorExample.hs +20/−0
+ ChangeLog.md view
@@ -0,0 +1,5 @@+# Revision history for microsoft-translator++## 0.1.0.0 -- YYYY-mm-dd++* First version. Released on an unsuspecting world.
+ LICENSE view
@@ -0,0 +1,20 @@+Copyright (c) 2017 Cliff Harvey++Permission is hereby granted, free of charge, to any person obtaining+a copy of this software and associated documentation files (the+"Software"), to deal in the Software without restriction, including+without limitation the rights to use, copy, modify, merge, publish,+distribute, sublicense, and/or sell copies of the Software, and to+permit persons to whom the Software is furnished to do so, subject to+the following conditions:++The above copyright notice and this permission notice shall be included+in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ microsoft-translator.cabal view
@@ -0,0 +1,46 @@+name: microsoft-translator+version: 0.1.0.0+synopsis: Bindings to the Microsoft Translator API+description: Bindings to the Microsoft Translator API+license: MIT+license-file: LICENSE+author: Cliff Harvey+maintainer: cs.hbar@gmail.com+copyright: Cliff Harvey 2017+category: Natural Language+build-type: Simple+extra-source-files: ChangeLog.md+cabal-version: >=1.10++library++ build-depends: base >= 4.10 && < 4.11+ , servant >= 0.11 && < 0.12+ , servant-client >= 0.11 && < 0.12+ , text >= 1.2 && < 1.3+ , http-client >= 0.5 && < 0.6+ , http-client-tls >= 0.3 && < 0.4+ , http-media >= 0.7 && < 0.8+ , bytestring >= 0.10 && < 0.11+ , http-api-data >= 0.3 && < 0.4+ , mtl >= 2.2 && < 2.3+ , time >= 1.8 && < 1.9+ , xml >= 1.3 && < 1.4+ , safe >= 0.3 && < 0.4++ hs-source-dirs: src+ default-language: Haskell2010++ exposed-modules: Microsoft.Translator.API.Auth+ , Microsoft.Translator.API+ , Microsoft.Translator+ , Microsoft.TranslatorExample++ other-modules: Microsoft.Translator.Exception+ , Microsoft.Translator.Language++ ghc-options: -Wall++source-repository head+ type: git+ location: https://github.com/BlackBrane/microsoft-translator
+ src/Microsoft/Translator.hs view
@@ -0,0 +1,253 @@+{-# LANGUAGE DeriveGeneric #-}++module Microsoft.Translator (++ -- * Basic Types+ SubscriptionKey (..)+ , AuthToken+ , AuthData (..)+ , TransData++ , Language (..)+ , TranslatorException++ , ArrayResponse (..)+ , TransItem (..)+ , Sentence (..)++ -- * API functions+ -- ** Authorization+ , lookupSubKey+ , issueToken+ , issueAuth+ , refresh+ , initTransData+ , initTransDataWith+ , checkAuth+ , keepFreshAuth++ -- ** Translation+ -- *** ExceptT variants+ , translate+ , translateArray+ , translateArrayText+ , translateArraySentences++ -- *** IO variants+ , lookupSubKeyIO+ , issueAuthIO+ , initTransDataIO+ , checkAuthIO+ , translateIO+ , translateArrayIO+ , translateArrayTextIO+ , translateArraySentencesIO++ -- *** Minimalistic variants+ , simpleTranslate+ , basicTranslate+ , basicTranslateArray++ -- * Pure functions+ , mkSentences++) where++import Microsoft.Translator.API+import Microsoft.Translator.API.Auth+import Microsoft.Translator.Exception++import Control.Concurrent (forkIO, threadDelay)+import Control.Monad.Except+import Data.Char (isSpace)+import Data.IORef+import Data.Monoid ((<>))+import Data.String (fromString)+import Data.Text as T (Text, all, splitAt)+import Data.Time+import GHC.Generics (Generic)+import Network.HTTP.Client+import Network.HTTP.Client.TLS+import System.Environment (lookupEnv)+++-- | Simplest possible translation function.+-- Always needs to make a request for the JWT token first.+simpleTranslate :: SubscriptionKey -> Manager+ -> Maybe Language -> Language+ -> Text -> IO (Either TranslatorException Text)+simpleTranslate key man from to txt = runExceptT $ do+ tok <- ExceptT $ issueToken man key+ ExceptT $ basicTranslate man tok from to txt++-- | Retrieve your subscription key from the TRANSLATOR_SUBSCRIPTION_KEY environment+-- variable.+lookupSubKey :: ExceptT TranslatorException IO SubscriptionKey+lookupSubKey = ExceptT $+ maybe (Left MissingSubscriptionKey) (Right . SubKey . fromString) <$>+ lookupEnv "TRANSLATOR_SUBSCRIPTION_KEY"++-- | Retrieve your subscription key from the TRANSLATOR_SUBSCRIPTION_KEY environment+-- variable.+lookupSubKeyIO :: IO (Either TranslatorException SubscriptionKey)+lookupSubKeyIO = runExceptT lookupSubKey+++-- | An 'AuthToken' together with the time it was recieved.+-- Each token is valid for 10 minutes.+data AuthData = AuthData+ { timeStamp :: UTCTime+ , authToken :: AuthToken+ } deriving Show+++-- | Retrieve a token, via 'issueToken', and save it together with a timestamp.+issueAuth :: Manager -> SubscriptionKey -> ExceptT TranslatorException IO AuthData+issueAuth man key = do+ tok <- ExceptT $ issueToken man key+ now <- liftIO getCurrentTime+ pure $ AuthData now tok+++-- | The data to hold onto for making translation requests.+-- Includes your 'SubscriptionKey', an `AuthData` and an HTTPS 'Manager'.+data TransData = TransData+ { subKey :: SubscriptionKey+ , manager :: Manager+ , authDataRef :: IORef AuthData }++-- | Retrieve an 'AuthData' token and hold on to the new HTTPS manager.+initTransData :: SubscriptionKey -> ExceptT TranslatorException IO TransData+initTransData key =+ liftIO (newManager tlsManagerSettings) >>= initTransDataWith key++-- | Retrieve an 'AuthData' token and hold on to the HTTPS manager.+-- For when you want to supply a particular manager. Otherwise use 'initTransData'.+initTransDataWith :: SubscriptionKey -> Manager -> ExceptT TranslatorException IO TransData+initTransDataWith key man =+ TransData key man <$> (issueAuth man key >>= liftIO . newIORef)+++refresh :: TransData -> ExceptT TranslatorException IO AuthData+refresh tdata = do+ auth <- issueAuth (manager tdata) (subKey tdata)+ liftIO $ writeIORef (authDataRef tdata) auth+ pure auth++-- | If a token contained in a 'TransData' is expired or about to expire, refresh it.+checkAuth :: TransData -> ExceptT TranslatorException IO AuthData+checkAuth tdata = do+ now <- liftIO getCurrentTime+ auth <- liftIO . readIORef $ authDataRef tdata+ if (diffUTCTime now (timeStamp auth) > 9*60+30)+ then refresh tdata+ else pure auth++-- | Create a 'TransData' with a new auth token and fork a thread to refresh it every+-- 9 minutes.+-- This is mostly a quick-and-dirty function for demo purposes and one-off projects.+-- You'll want to roll something more robust for production applications.+keepFreshAuth :: SubscriptionKey -> ExceptT TranslatorException IO TransData+keepFreshAuth key = do+ tdata <- initTransData key+ _ <- liftIO . forkIO $ loop tdata+ pure tdata++ where+ loop :: TransData -> IO ()+ loop td = do+ threadDelay $ 10^(6::Int) * 9 * 60+ _ <- runExceptT $ refresh td+ loop td+++-- | Translate text+translate :: TransData -> Maybe Language -> Language -> Text+ -> ExceptT TranslatorException IO Text+translate tdata from to txt = do+ tok <- authToken <$> checkAuth tdata+ ExceptT $ basicTranslate (manager tdata) tok from to txt++-- | Translate a text array.+-- The 'ArrayResponse' you get back includes sentence break information.+translateArray :: TransData -> Language -> Language -> [Text]+ -> ExceptT TranslatorException IO ArrayResponse+translateArray tdata from to txts = do+ tok <- authToken <$> checkAuth tdata+ ExceptT $ basicTranslateArray (manager tdata) tok from to txts++-- | Translate a text array, and just return the list of texts.+translateArrayText :: TransData -> Language -> Language -> [Text]+ -> ExceptT TranslatorException IO [Text]+translateArrayText tdata from to txts =+ map transText . getArrayResponse <$> translateArray tdata from to txts++-- | Translate a text array, and split all the texts into constituent sentences,+-- paired with the originals.+translateArraySentences :: TransData -> Language -> Language -> [Text]+ -> ExceptT TranslatorException IO [[Sentence]]+translateArraySentences tdata from to txts =+ mkSentences txts <$> translateArray tdata from to txts++-- | An original/translated sentence pair.+data Sentence = Sentence+ { fromText :: Text+ , toText :: Text+ } deriving (Show, Eq, Generic)++extractSentences :: [Int] -> Text -> [Text]+extractSentences [] txt = [txt]+extractSentences (n:ns) txt = headTxt : extractSentences ns tailTxt+ where (headTxt, tailTxt) = T.splitAt n txt++-- | Take the original texts and the ArrayResponse object, and apply the sentence break+-- information to pair each sentence in the request to the translated text.+mkSentences :: [Text] -> ArrayResponse -> [[Sentence]]+mkSentences origTxts (ArrayResponse tItems) =+ uncurry formSentenceSet <$> zip origTxts tItems+ where+ formSentenceSet :: Text -> TransItem -> [Sentence]+ formSentenceSet origTxt (TransItem transTxt origBreaks transBreaks) =+ filter notBlank $ zipWith Sentence+ (extractSentences origBreaks origTxt)+ (extractSentences transBreaks transTxt)++ notBlank :: Sentence -> Bool+ notBlank (Sentence orig trans) = not . T.all isSpace $ orig <> trans++++-- | Retrieve a token, via 'issueToken', and save it together with a timestamp.+issueAuthIO :: Manager -> SubscriptionKey -> IO (Either TranslatorException AuthData)+issueAuthIO man = runExceptT . issueAuth man++-- | Retrieve an 'AuthData' token and start up an HTTPS manager.+initTransDataIO :: SubscriptionKey -> IO (Either TranslatorException TransData)+initTransDataIO = runExceptT . initTransData++-- | If a token contained in a 'TransData' is expired or about to expire, refresh it.+checkAuthIO :: TransData -> IO (Either TranslatorException AuthData)+checkAuthIO = runExceptT . checkAuth++-- | Translate text.+translateIO :: TransData -> Maybe Language -> Language -> Text+ -> IO (Either TranslatorException Text)+translateIO tdata from to = runExceptT . translate tdata from to++-- | Translate a text array.+translateArrayIO :: TransData -> Language -> Language -> [Text]+ -> IO (Either TranslatorException ArrayResponse)+translateArrayIO tdata from to = runExceptT . translateArray tdata from to++-- | Translate a text array, and just return the list of texts.+translateArrayTextIO :: TransData -> Language -> Language -> [Text]+ -> IO (Either TranslatorException [Text])+translateArrayTextIO tdata from to =+ runExceptT . translateArrayText tdata from to++-- | Translate a text array, and split all the texts into constituent sentences+-- paired with the originals.+translateArraySentencesIO :: TransData -> Language -> Language -> [Text]+ -> IO (Either TranslatorException [[Sentence]])+translateArraySentencesIO tdata from to =+ runExceptT . translateArraySentences tdata from to
+ src/Microsoft/Translator/API.hs view
@@ -0,0 +1,173 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE ViewPatterns #-}++module Microsoft.Translator.API (++ basicTranslate+ , basicTranslateArray+ , TranslatorException+ , Language (..)+ , ArrayRequest (..)+ , ArrayResponse (..)+ , TransItem (..)++) where++import Microsoft.Translator.API.Auth+import Microsoft.Translator.Exception+import Microsoft.Translator.Language++import Data.Bifunctor+import Data.ByteString.Lazy (fromStrict, toStrict)+import Data.Monoid+import Data.Proxy+import Data.Text as T (Text, pack, unlines)+import Data.Text.Encoding (decodeUtf8', encodeUtf8)+import Data.Typeable+import GHC.Generics (Generic)+import Network.HTTP.Client hiding (Proxy)+import qualified Network.HTTP.Media as M+import Safe (headMay, readMay)+import Servant.API+import Servant.Client+import Text.XML.Light.Input+import Text.XML.Light.Proc+import Text.XML.Light.Types+++baseUrl :: BaseUrl+baseUrl = BaseUrl Https "api.microsofttranslator.com" 443 "/V2/Http.svc"++-- | MS Microsoft.Translator API+-- http://docs.microsofttranslator.com/text-translate.html#!/default/get_Translate+type API =+ "Translate"+ :> Header "authorization" AuthToken+ :> QueryParam "text" Text+ :> QueryParam "from" Language+ :> QueryParam "to" Language+ :> Get '[XML] TransText+ :<|> "TranslateArray"+ :> Header "authorization" AuthToken+ :> ReqBody '[XML] ArrayRequest+ :> Post '[XML] ArrayResponse+++newtype TransText = TransText { getTransText :: Text }+ deriving Generic++data ArrayRequest = ArrayRequest+ { fromLang :: Language+ , toLang :: Language+ , texts :: [Text]+ }++newtype ArrayResponse = ArrayResponse+ { getArrayResponse :: [TransItem] }+ deriving (Show,Generic)++data TransItem = TransItem+ { transText :: Text+ , originalBreaks :: [Int]+ , translatedBreaks :: [Int]+ } deriving (Show, Generic)+++-- | JSON Web Token content type+data XML+ deriving Typeable++instance Accept XML where+ contentType _ = "application" M.// "xml" M./: ("charset", "utf-8")++instance MimeUnrender XML TransText where+ mimeUnrender _ bs = first show $ do+ txt <- first InvalidUTF8 . decodeUtf8' $ toStrict bs+ el <- maybe (Left $ InvalidXML txt) Right $ parseXMLDoc txt+ maybe (Left $ UnexpectedXMLLayout el) Right $ extractTransResponse el++extractTransResponse :: Element -> Maybe TransText+extractTransResponse+ = fmap (TransText . pack . cdData)+ . headMay+ . onlyText+ . elContent++encodeRequestXML :: ArrayRequest -> Text+encodeRequestXML (ArrayRequest from to txts) = T.unlines $+ [ "<TranslateArrayRequest>"+ , " <AppId />"+ , " <From>" <> toLangCode from <> "</From>"+ , " <Texts>"+ ] <> fmap xmlString txts <>+ [ " </Texts>"+ , " <To>" <> toLangCode to <> "</To>"+ , "</TranslateArrayRequest>"+ ]++ where+ xmlString str =+ "<string xmlns=\"http://schemas.microsoft.com/2003/10/Serialization/Arrays\">" <>+ str <> "</string>"++instance MimeRender XML ArrayRequest where+ mimeRender _ = fromStrict . encodeUtf8 . encodeRequestXML++instance MimeUnrender XML ArrayResponse where+ mimeUnrender _ bs = first show $ do+ txt <- first InvalidUTF8 . decodeUtf8' $ toStrict bs+ el <- maybe (Left $ InvalidXML txt) Right $ parseXMLDoc txt+ extractArrayResponse el++extractArrayResponse :: Element -> Either TranslatorException ArrayResponse+extractArrayResponse+ = fmap ArrayResponse+ . traverse extractItem+ . onlyElems+ . elContent++extractItem :: Element -> Either TranslatorException TransItem+extractItem el@(onlyElems . elContent -> l) =+ maybe (Left $ UnexpectedXMLLayout el) Right $ do+ txtElem <- headMay [ x | x <- l, qName (elName x) == "TranslatedText" ]+ origSep <- extractBreaks =<<+ headMay [ x | x <- l, qName (elName x) == "OriginalTextSentenceLengths" ]+ transSep <- extractBreaks =<<+ headMay [ x | x <- l, qName (elName x) == "TranslatedTextSentenceLengths" ]+ cd <- headMay . onlyText $ elContent txtElem+ Just $ TransItem (pack $ cdData cd) origSep transSep++extractBreaks :: Element -> Maybe [Int]+extractBreaks+ = traverse readMay+ . fmap cdData+ . onlyText+ . concatMap elContent+ . onlyElems+ . elContent+++transClient :: Maybe AuthToken -> Maybe Text -> Maybe Language -> Maybe Language -> ClientM TransText+arrayClient :: Maybe AuthToken -> ArrayRequest -> ClientM ArrayResponse+transClient :<|> arrayClient = client (Proxy @ API)++basicTranslate :: Manager -> AuthToken -> Maybe Language -> Language -> Text+ -> IO (Either TranslatorException Text)+basicTranslate man tok from to txt =+ bimap APIException getTransText <$>+ runClientM+ (transClient (Just tok) (Just txt) from (Just to))+ (ClientEnv man baseUrl)++basicTranslateArray :: Manager -> AuthToken -> Language -> Language -> [Text]+ -> IO (Either TranslatorException ArrayResponse)+basicTranslateArray man tok from to txts =+ bimap APIException id <$>+ runClientM+ (arrayClient (Just tok) (ArrayRequest from to txts))+ (ClientEnv man baseUrl)
+ src/Microsoft/Translator/API/Auth.hs view
@@ -0,0 +1,77 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeOperators #-}++module Microsoft.Translator.API.Auth (++ SubscriptionKey (..)+ , AuthToken+ , TranslatorException+ , issueToken++) where++import Microsoft.Translator.Exception++import Control.Arrow (left)+import Data.Bifunctor+import Data.ByteString.Lazy (toStrict)+import Data.Monoid+import Data.String+import Data.Text (Text)+import Data.Text.Encoding (decodeUtf8')+import Data.Typeable+import GHC.Generics (Generic)+import Network.HTTP.Client hiding (Proxy)+import qualified Network.HTTP.Media as M+import Servant.API+import Servant.Client+++authUrl :: BaseUrl+authUrl = BaseUrl Https "api.cognitive.microsoft.com" 443 "/sts/v1.0"++-- | MS Microsoft.Translator token service API+-- http://docs.microsofttranslator.com/oauth-token.html+type AuthAPI =+ "issueToken"+ :> QueryParam "Subscription-Key" SubscriptionKey+ :> Post '[JWT] AuthToken++-- | A key to your subscription to the service. Used to retrieve an 'AuthToken'.+newtype SubscriptionKey+ = SubKey Text+ deriving (Show, ToHttpApiData, IsString)++-- | The JSON Web Token issued by MS Microsoft.Translator token service. Consists of wrapped text.+-- Valid for ten minutes.+newtype AuthToken+ = AuthToken Text+ deriving (Show, Generic)++-- | JSON Web Token content type+data JWT+ deriving Typeable++instance Accept JWT where+ contentType _ = "application" M.// "jwt" M./: ("charset", "us-ascii")++instance MimeUnrender JWT AuthToken where+ mimeUnrender _ = fmap AuthToken . left show . decodeUtf8' . toStrict++instance ToHttpApiData AuthToken where+ toUrlPiece (AuthToken txt) = "Bearer " <> txt+++authClient :: Maybe SubscriptionKey -> ClientM AuthToken+authClient = client (Proxy @ AuthAPI)++-- | Retrieve a token from the API. It will be valid for 10 minutes.+issueToken :: Manager -> SubscriptionKey -> IO (Either TranslatorException AuthToken)+issueToken man key = first APIException <$>+ runClientM (authClient $ Just key) (ClientEnv man authUrl)
+ src/Microsoft/Translator/Exception.hs view
@@ -0,0 +1,21 @@+{-# LANGUAGE DeriveDataTypeable #-}++module Microsoft.Translator.Exception where++import Control.Exception+import Data.Text+import Data.Text.Encoding.Error+import Data.Typeable+import Servant.Client+import Text.XML.Light.Types+++data TranslatorException+ = APIException ServantError+ | InvalidXML Text+ | UnexpectedXMLLayout Element+ | InvalidUTF8 UnicodeException+ | MissingSubscriptionKey+ deriving (Show, Typeable)++instance Exception TranslatorException
+ src/Microsoft/Translator/Language.hs view
@@ -0,0 +1,122 @@+{-# LANGUAGE OverloadedStrings #-}++module Microsoft.Translator.Language where++import Data.Text+import Web.HttpApiData+++-- | Languages supported by MS Microsoft.Translator+data Language+ = Afrikaans+ | Arabic+ | Bosnian+ | Bulgarian+ | Catalan+ | ChineseSimplified+ | ChineseTraditional+ | Croatian+ | Czech+ | Danish+ | Dutch+ | English+ | Estonian+ | Finnish+ | French+ | German+ | Greek+ | HaitianCreole+ | Hebrew+ | Hindi+ | HmongDaw+ | Hungarian+ | Indonesian+ | Italian+ | Japanese+ | Kiswahili+ | Klingon+ | KlingonPIqaD+ | Korean+ | Latvian+ | Lithuanian+ | Malay+ | Maltese+ | Norwegian+ | Persian+ | Polish+ | Portuguese+ | QueretaroOtomi+ | Romanian+ | Russian+ | SerbianCyrillic+ | SerbianLatin+ | Slovak+ | Slovenian+ | Spanish+ | Swedish+ | Thai+ | Turkish+ | Ukrainian+ | Urdu+ | Vietnamese+ | Welsh+ | YucatecMaya+++toLangCode :: Language -> Text+toLangCode Afrikaans = "af"+toLangCode Arabic = "ar"+toLangCode Bosnian = "bs-Latn"+toLangCode Bulgarian = "bg"+toLangCode Catalan = "ca"+toLangCode ChineseSimplified = "zh-CHS"+toLangCode ChineseTraditional = "zh-CHT"+toLangCode Croatian = "hr"+toLangCode Czech = "cs"+toLangCode Danish = "da"+toLangCode Dutch = "nl"+toLangCode English = "en"+toLangCode Estonian = "et"+toLangCode Finnish = "fi"+toLangCode French = "fr"+toLangCode German = "de"+toLangCode Greek = "el"+toLangCode HaitianCreole = "ht"+toLangCode Hebrew = "he"+toLangCode Hindi = "hi"+toLangCode HmongDaw = "mww"+toLangCode Hungarian = "hu"+toLangCode Indonesian = "id"+toLangCode Italian = "it"+toLangCode Japanese = "ja"+toLangCode Kiswahili = "sw"+toLangCode Klingon = "tlh"+toLangCode KlingonPIqaD = "tlh-Qaak"+toLangCode Korean = "ko"+toLangCode Latvian = "lv"+toLangCode Lithuanian = "lt"+toLangCode Malay = "ms"+toLangCode Maltese = "mt"+toLangCode Norwegian = "no"+toLangCode Persian = "fa"+toLangCode Polish = "pl"+toLangCode Portuguese = "pt"+toLangCode QueretaroOtomi = "otq"+toLangCode Romanian = "ro"+toLangCode Russian = "ru"+toLangCode SerbianCyrillic = "sr-Cyrl"+toLangCode SerbianLatin = "sr-Latn"+toLangCode Slovak = "sk"+toLangCode Slovenian = "sl"+toLangCode Spanish = "es"+toLangCode Swedish = "sv"+toLangCode Thai = "th"+toLangCode Turkish = "tr"+toLangCode Ukrainian = "uk"+toLangCode Urdu = "ur"+toLangCode Vietnamese = "vi"+toLangCode Welsh = "cy"+toLangCode YucatecMaya = "yua"++instance ToHttpApiData Language where+ toUrlPiece = toLangCode
+ src/Microsoft/TranslatorExample.hs view
@@ -0,0 +1,20 @@+{-# LANGUAGE OverloadedStrings #-}++module Microsoft.TranslatorExample where++import Microsoft.Translator++import Control.Monad.Except+import qualified Data.Text.IO as T+++main :: IO ()+main = do+ -- set your subscription key in the TRANSLATOR_SUBSCRIPTION_KEY environment var+ Right transData <- runExceptT (lookupSubKey >>= initTransData)+ forever $ do+ T.putStr "> "+ str <- T.getLine+ Right txt <- translateIO transData Nothing ChineseTraditional str+ T.putStrLn txt+ T.putStrLn ""