Translating text to another language with DeepL's REST API

I Love Xbase++ (ILX)
The portal for Xbase++ developers worldwide

Till Warweg

Member
Staff member
I am here to help you!
Feb 1, 2021
38
11
8
Customer Identifier
E074775

Background​

Sometimes it is required to translate text from one language to another in an Xbase++ application. This article outlines the steps required to meet this requirement using DeepL's REST API.

Note that DeepL provides many more services, for example, translations of whole documents (HTML, XML). Although not shown here, using these services should require only minor changes in the provided example.

Step 1: Create a DeepL account and sign up for the free service​

As a first step, we'll need to sign up for a DeepL account in order to be able to the translation services. We'll use a free account in this example, which comes with limitations (500.000 characters free per month at the time of this writing) but should be sufficient testing.

Go to this page to sign up for a DeepL account: https://www.deepl.com/de/signup?cta=free-login-signup.
You'll be required to provide a user name and password. Once the account has been created, you'll need to select the plan/paket you'll be using to access the translation service. Go to the corresponding page, select "DeepL API" and sign up for the free plan by clicking on the corresponding button. This requires you to provide credit card information for verification purposes.

Step 2: Create an API key for client authentication​

The client application will need to authenticate itself to DeepL's translation service by providing a so-called API key, which you'll need to set up/maintain via your DeepL account. An API key should already have been set up for your free account. You'll find it on the API Keys page in your DeepL account.

1729787059828.png


Note/copy your API key on this page. It must be specified in the code shown below.

Step 3: Construct a text translation request and send it​

Translating a text is fairly easy using the DeepLClient class provided below. The class hides the internals of dealing with the REST API and provides simple interfaces for constructing and sending a text translation request to DeepL's translation service.

Xbase++:
// API key created in the DeepL account/admin interface
#define API_KEY "228f*******6:fx!

PROCEDURE Main()
 LOCAL oC
 LOCAL cText

   SET CHARSET TO ANSI

   oC := DeepLClient():new( API_KEY )

   // Translate an English text to German and Spanish
   cText := "The quick brown fox jumps over the lazy dog"
   cTrans:= oC:translateText( cText,, "DE" )
   IF oC:getLastError() == 0
      ? "The German text reads: ", cTrans
   ELSE
      ? "Error. The fox is quick but doesn't speak German."
   ENDIF
 
   cTrans := oC:translateText( cText,, "ES" )
   IF oC:getLastError() == 0
      ? "The Spanish text reads: ", cTrans
   ELSE
      ? "Error. No quick fox in Spanish either."
   ENDIF
RETURN

Implementation of the "DeepLClient" class​

The following is the implementation of the DeepLClient class for translating text via DeepL's translation service. The implementation is intended as an example and uses only a bare minimum of the available options. The translation API has many more features, such as the ability to preserve formatting in the text or to control the formality/tone of the response. See the REST API specs. to see what's available.

Xbase++:
// URL of the free DeepL translation service
// Note: DeepL Pro accounts/services use a different URL:  https://api.deepl.com
#define SERVICE_URI  "https://api-free.deepl.com"

CLASS DeepLClient
 PROTECTED:
   VAR    _client
   VAR    _text
   VAR    _langSrc
   VAR    _billedChars
   VAR    _lastError
   VAR    _lastMessage

 EXPORTED:
   METHOD init()

   METHOD translateText()
   METHOD getSourceLang()
   METHOD getBilledChars()

   METHOD getLastError()
   METHOD getLastMessage()
ENDCLASS

/// <summary>Initialize the DeepL client</summary>
/// <param name="cAPIKey">
/// API key, see DeepL account/admin interface
/// </param>
METHOD DeepLClient:init( cAPIKey )
   ::_client := HttpClient():new( SERVICE_URI + "/v2/translate" )

   ::_client:setMethod( "POST" )
   ::_client:httpRequest:setContentType( "application/json" )
   ::_client:httpRequest:setHeader( "Authorization", "DeepL-Auth-Key " + cAPIKey )

   ::_lastError   := 0
   ::_lastMessage := ""
   ::_text        := ""
   ::_langSrc     := ""
   ::_billedChars := 0
RETURN self

/// <summary>Translate a text from an optional source to a destination
/// language</summary>
/// <param name="cText">The message text</param>
/// <param name="cLangSrc">Optional source language as an ISO 3166
/// country code. If omitted, the API tries to deduct the language
/// from the given text</param>
/// <param name="cLangDst">Destination language to translate to as an
/// ISO 3166 country code</param>
/// <returns>The translated text. If something goes wrong, return is
/// the empty string</returns>
METHOD DeepLClient:translateText( cText, cLangSrc, cLangDst )
 LOCAL data
 LOCAL cContent

   // Construct a JSON object with the text and the desired
   // translation options to be sent with the request. See the API
   // specs for the available options.

   IF Empty(cText) .OR. Empty(cLangDst)
      RETURN ""
   ENDIF

   ::_lastError   := 0
   ::_lastMessage := ""
   ::_langSrc     := ""
   ::_billedChars := 0

   data := DataObject():new()
   // Note: DeepL supports processing several texts at once. This isn't used here.
   data:text := {Char2UTF8( cText )}

   IF !Empty(cLangSrc)
      data:source_lang := cLangSrc
      ::_langSrc := cLangSrc
   ENDIF

   data:target_lang := cLangDst
   data:show_billed_characters := .T.

   cContent := Var2JSON( data )
   ::_client:httpRequest:setContent( cContent )
   cResult := ::_client:send()

   IF .NOT. Empty(cResult)
      oResult := JSON2Var( cResult )
   ENDIF

   // Check and store the status code of the operation for retrieval
   // via :getLastError(). Also, save an eventual error message for
   // :getLastMessage(). See the API spec. for the defined error
   // codes.
   nCode := ::_client:getStatusCode()
   IF nCode != 200
      ::_lastError := nCode
      IF .NOT. IsNull(oResult) .AND. .NOT. Empty(oResult:message)
         ::_lastMessage := oResult:message
      ENDIF
      RETURN ""
   ENDIF

   cText := ""

   IF Empty(oResult:translations)
      RETURN ""
   ENDIF

   // We've translated only a single text, see above
   oResult := oResult:translations[1]
   IF !Empty(oResult:detected_source_language)
      ::_langSrc := oResult:detected_source_language
   ENDIF

   IF !Empty(oResult:billed_characters)
      ::_billedChars := oResult:billed_characters
   ENDIF

   IF !Empty(oResult:text)
      cText := oResult:text
   ENDIF
RETURN cText

/// <summary>Get the error code of the last operation, if any</summary>
/// <returns>0 for "no error", or one of the defined error/status codes.
/// See the API specs</returns>
METHOD DeepLClient:getLastError()
RETURN ::_lastError

/// <summary>Get the error message of the last operation, if any</summary>
/// <returns>A string with the error message or "", if no error message
/// is available</returns>
METHOD DeepLClient:getLastMessage()
RETURN ::_lastMessage

/// <summary>Get the source language used for the translation. If none
/// was specified by the caller, the source language was deducted by API.
/// </summary>
/// <returns>ISO 3166 language identifier</returns>
METHOD DeepLClient:getSourceLang()
RETURN ::_langSrc

/// <summary>Get the number of characters used by DeepL for billing purposes
/// </summary>
/// <returns>Character count</returns>
METHOD DeepLClient:getBilledChars()
RETURN ::_billedChars

References:​

 
Last edited:
  • Like
Reactions: Osvaldo Ramirez