module Microsoft.Translator (
SubscriptionKey (..)
, AuthToken
, AuthData (..)
, TransData
, Language (..)
, TranslatorException
, ArrayResponse (..)
, TransItem (..)
, Sentence (..)
, lookupSubKey
, issueToken
, issueAuth
, refresh
, initTransData
, initTransDataWith
, checkAuth
, keepFreshAuth
, translate
, translateArray
, translateArrayText
, translateArraySentences
, lookupSubKeyIO
, issueAuthIO
, initTransDataIO
, checkAuthIO
, translateIO
, translateArrayIO
, translateArrayTextIO
, translateArraySentencesIO
, simpleTranslate
, basicTranslate
, basicTranslateArray
, 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)
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
lookupSubKey :: ExceptT TranslatorException IO SubscriptionKey
lookupSubKey = ExceptT $
maybe (Left MissingSubscriptionKey) (Right . SubKey . fromString) <$>
lookupEnv "TRANSLATOR_SUBSCRIPTION_KEY"
lookupSubKeyIO :: IO (Either TranslatorException SubscriptionKey)
lookupSubKeyIO = runExceptT lookupSubKey
data AuthData = AuthData
{ timeStamp :: UTCTime
, authToken :: AuthToken
} deriving Show
issueAuth :: Manager -> SubscriptionKey -> ExceptT TranslatorException IO AuthData
issueAuth man key = do
tok <- ExceptT $ issueToken man key
now <- liftIO getCurrentTime
pure $ AuthData now tok
data TransData = TransData
{ subKey :: SubscriptionKey
, manager :: Manager
, authDataRef :: IORef AuthData }
initTransData :: SubscriptionKey -> ExceptT TranslatorException IO TransData
initTransData key =
liftIO (newManager tlsManagerSettings) >>= initTransDataWith key
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
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
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 :: 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
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
translateArrayText :: TransData -> Language -> Language -> [Text]
-> ExceptT TranslatorException IO [Text]
translateArrayText tdata from to txts =
map transText . getArrayResponse <$> translateArray tdata from to txts
translateArraySentences :: TransData -> Language -> Language -> [Text]
-> ExceptT TranslatorException IO [[Sentence]]
translateArraySentences tdata from to txts =
mkSentences txts <$> translateArray tdata from to txts
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
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
issueAuthIO :: Manager -> SubscriptionKey -> IO (Either TranslatorException AuthData)
issueAuthIO man = runExceptT . issueAuth man
initTransDataIO :: SubscriptionKey -> IO (Either TranslatorException TransData)
initTransDataIO = runExceptT . initTransData
checkAuthIO :: TransData -> IO (Either TranslatorException AuthData)
checkAuthIO = runExceptT . checkAuth
translateIO :: TransData -> Maybe Language -> Language -> Text
-> IO (Either TranslatorException Text)
translateIO tdata from to = runExceptT . translate tdata from to
translateArrayIO :: TransData -> Language -> Language -> [Text]
-> IO (Either TranslatorException ArrayResponse)
translateArrayIO tdata from to = runExceptT . translateArray tdata from to
translateArrayTextIO :: TransData -> Language -> Language -> [Text]
-> IO (Either TranslatorException [Text])
translateArrayTextIO tdata from to =
runExceptT . translateArrayText tdata from to
translateArraySentencesIO :: TransData -> Language -> Language -> [Text]
-> IO (Either TranslatorException [[Sentence]])
translateArraySentencesIO tdata from to =
runExceptT . translateArraySentences tdata from to