diff --git a/pkg/api/http_client.go b/pkg/api/http_client.go index 03095bf..8490267 100644 --- a/pkg/api/http_client.go +++ b/pkg/api/http_client.go @@ -8,16 +8,11 @@ import ( "time" ) -const ( - BaseURLPro = "https://api.deepl.com/v2" - BaseURLFree = "https://api-free.deepl.com/v2" -) - type Client struct { httpClient *http.Client } -func NewClient(baseURL string, authKey string, timeout time.Duration) *Client { +func NewClient(timeout time.Duration) *Client { client := &http.Client{ Timeout: timeout, } diff --git a/pkg/api/translate.go b/pkg/api/translate.go index 0e53eb4..57d1389 100644 --- a/pkg/api/translate.go +++ b/pkg/api/translate.go @@ -4,6 +4,8 @@ import ( "encoding/json" "net/http" "net/url" + + "github.com/pkg/errors" ) type Translation struct { @@ -11,80 +13,101 @@ type Translation struct { Text string `json:"text"` } +// TranslateOption is a functional option for configuring text translation parameters type TranslateOption func(url.Values) // The language to be translated. -// If this parameter is omitted, the API will attempt to detect the language of the text and translate it. -func SourceLang(lang string) TranslateOption { +// If this parameter is omitted, the API will attempt to detect the language of the text and translate it +func SourceLang(lang string) (TranslateOption, error) { return func(vals url.Values) { vals.Set("source_lang", lang) - } + }, nil } -// Sets whether the translation engine should first split the input into sentences. -func SplitSentences(split string) TranslateOption { - return func(vals url.Values) { - vals.Set("split_sentences", split) +// SplitSentences sets whether the translation engine should first split the input into sentences +func SplitSentences(split string) (TranslateOption, error) { + switch split { + case "0", "1", "nonewlines": + return func(vals url.Values) { + vals.Set("split_sentences", split) + }, nil } + return nil, errors.Errorf("Invalid SplitSentence value: %s", split) } -// Sets whether the translation engine should respect the original formatting, even if it would usually correct some aspects. -func PreserveFormatting(preserve string) TranslateOption { - return func(vals url.Values) { - vals.Set("preserve_formatting", preserve) +// PreserveFormatting sets whether the translation engine should respect the original formatting, even if it would usually correct some aspects +func PreserveFormatting(preserve string) (TranslateOption, error) { + switch preserve { + case "0", "1": + return func(vals url.Values) { + vals.Set("preserve_formatting", preserve) + }, nil } + return errors.Errorf("Invalid PreserveFormatting value: %s", preserve) } -// Sets whether the translated text should lean towards formal or informal language. -func Formality(formality string) TranslateOption { - return func(vals url.Values) { - vals.Set("formality", formality) +// Formality sets whether the translated text should lean towards formal or informal language +func Formality(formality string) (TranslateOption, error) { + switch formality { + case "default", "more", "less", "prefer_more", "prefer_less": + return func(vals url.Values) { + vals.Set("formality", formality) + }, nil } + return nil, errors.Errorf("Invalid Formality value: %s", formality) } -// Specify the glossary to use for the translation. -func GlossaryId(glossary string) TranslateOption { +// GlossaryId specifies the glossary to use for the translation +func GlossaryId(glossary string) (TranslateOption, error) { return func(vals url.Values) { vals.Set("glossary_id", glossary) - } + }, nil } -// Sets which kind of tags should be handled. -func TagHandling(handling string) TranslateOption { - return func(vals url.Values) { - vals.Set("tag_handling", handling) +// TagHandling sets which kind of tags should be handled +func TagHandling(handling string) (TranslateOption, error) { + switch handling { + case "html", "xml": + return func(vals url.Values) { + vals.Set("tag_handling", handling) + }, nil } + return nil, errors.Errorf("Invalid TagHandling value: %s", handling) } -// Comma-separated list of XML tags which never split sentences. -func NonSplittingTags(tags string) TranslateOption { +// NonSplittingTags specifies a comma-separated list of XML tags which never split sentences +func NonSplittingTags(tags string) (TranslateOption, error) { return func(vals url.Values) { vals.Set("non_splitting_tags", tags) - } + }, nil } -// Disable the automatic detection of the XML structure. -func OutlineDetection(detect string) TranslateOption { - return func(vals url.Values) { - vals.Set("outline_detection", detect) +// OutlineDetection can be used to disable the automatic detection of the XML structure +func OutlineDetection(detect string) (TranslateOption, error) { + switch detect { + case "0": + return func(vals url.Values) { + vals.Set("outline_detection", detect) + }, nil } + return nil, errors.Errorf("Invalid OutlineDetection value: %s", detect) } -// Comma-separated list of XML tags which always cause splts. -func SplittingTags(tags string) TranslateOption { +// SplittingTags specifies a comma-separated list of XML tags which always cause splts +func SplittingTags(tags string) (TranslateOption, error) { return func(vals url.Values) { vals.Set("splitting_tags", tags) - } + }, nil } -// Comma-separated list of XML tags that indicate text not to be translated. -func IgnoreTags(tags string) TranslateOption { +// IgnoeTags specifies a comma-separated list of XML tags that indicate text not to be translated +func IgnoreTags(tags string) (TranslateOption, error) { return func(vals url.Values) { vals.Set("ignore_tags", tags) - } + }, nil } -// The translate function. +// TranslateText translates the given text(s) into the specified target language func (t *Translator) TranslateText(texts []string, targetLang string, options ...TranslateOption) ([]Translation, error) { vals := make(url.Values) @@ -94,6 +117,7 @@ func (t *Translator) TranslateText(texts []string, targetLang string, options .. vals.Set("target_lang", targetLang) + // Apply translation parameter options for _, opt := range options { opt(vals) } diff --git a/pkg/api/translator.go b/pkg/api/translator.go index 04c53be..156512f 100644 --- a/pkg/api/translator.go +++ b/pkg/api/translator.go @@ -8,9 +8,10 @@ import ( "time" ) -type TranslatorOptions struct { - ServerUrl string `default:""` -} +const ( + ServerURLPro = "https://api.deepl.com/v2" + ServerURLFree = "https://api-free.deepl.com/v2" +) type Translator struct { httpClient *Client @@ -18,26 +19,58 @@ type Translator struct { authKey string } -func NewTranslator(authKey string, options TranslatorOptions) *Translator { - var serverURL string - if options.ServerUrl == "" { - if authKeyIsFreeAccount(authKey) { - serverURL = BaseURLFree - } else { - serverURL = BaseURLPro +// TranslatorOption is a functional option for configuring the Translator +type TranslatorOption func(*Translator) error + +// ServerURL allows overriding the default server url +func ServerURL(url string) TranslatorOption { + return func(t *Translator) error { + t.serverURL = url + return nil + } +} + +// parseOptions apllies the supplied functional options to the Translator +func (t *Translator) parseOptions(opts ...TranslatorOption) error { + for _, opt := range opts { + err := opt(t) + if err != nil { + return err } } - timeout := 10 * time.Second - httpClient := NewClient(serverURL, authKey, timeout) + return nil +} - return &Translator{ +// NewTranslator creates a new translator +func NewTranslator(authKey string, opts ...TranslatorOption) (*Translator, error) { + // Determine default server url based on auth key + var serverURL string + if authKeyIsFreeAccount(authKey) { + serverURL = ServerURLFree + } else { + serverURL = ServerURLPro + } + + // Set up default http client + timeout := time.Second * 30 + httpClient := NewClient(timeout) + + t := &Translator{ httpClient: httpClient, serverURL: serverURL, authKey: authKey, } + + // Parse and apply options + if err := t.parseOptions(opts...); err != nil { + return nil, err + } + + return t, nil } +// callAPI calls the supplied API endpoint with the provided parameters and returns the response func (t *Translator) callAPI(method string, endpoint string, data url.Values, headers http.Header) (*http.Response, error) { url := fmt.Sprintf("%s/%s", t.serverURL, endpoint) @@ -54,6 +87,7 @@ func (t *Translator) callAPI(method string, endpoint string, data url.Values, he return res, err } +// authKeyIsFreeAccount determines whether the supplied auth key belongs to a Free account func authKeyIsFreeAccount(authKey string) bool { return strings.HasSuffix(authKey, ":fx") }