
BTP - Google Translate Integration
How do you call an external API, such as Google Translate, from the ABAP environment and consume it in a local class? Read more in this article.
Table of contents
We have already looked at the provisioning of the Google Translate API and how you can access it in an older article.
Introduction
The BTP provides numerous services, but what about the integration of other services? The integration of REST-based services can be done relatively easily using the new HTTP classes in the system. The Translate API only needs one call and sends us the translated tokens back.
Implementation
In this section we want to implement the API call and explain everything to you step by step.
URL
In the first step we need the URL of the API with the API key so that we can use the service without authentication. To do this we create two constants globally.
CONSTANTS c_api_endpoint TYPE string VALUE 'https://translation.googleapis.com/language/translate/v2'.
CONSTANTS c_api_key TYPE string VALUE ''.
We then use a string template to build the final URL; after the endpoint comes the parameter with the API key.
rd_url = |{ c_api_endpoint }?key={ c_api_key }|.
Payload
In the next step, we generate the payload, which is the request to Google Translate. To do this, we define a type for the structure. You can find the structure of the request in the API documentation.
TYPES: BEGIN OF ts_google_request,
q TYPE string_table,
target TYPE string,
END OF ts_google_request.
To do this, we pass the texts to be translated and the target language to the structure and generate the JSON for the request. We call the SERIALIZE method and pass a mapping for the fields to get the correct lowercase field names.
DATA(ls_google_request) = VALUE ts_google_request( q = it_text
target = id_target_language ).
rd_result = /ui2/cl_json=>serialize( data = ls_google_request
name_mappings = VALUE #( ( abap = 'Q' json = 'q' )
( abap = 'TARGET' json = 'target' ) ) ).
The payload should now look something like this:
{
"q": [
"Test Artikel",
"Bitte diesen Text ins Englische übersetzen."
],
"target": "en"
}
Request
Now we want to create the request. To do this, we first need to create a destination with the URL and then a client object using the client manager.
DATA(lo_destination) = cl_http_destination_provider=>create_by_url( ld_url ).
DATA(lo_client) = cl_web_http_client_manager=>create_by_http_destination( lo_destination ).
We get a request from the client and configure it by setting the ContentType and specifying the payload.
DATA(lo_request) = lo_client->get_http_request( ).
lo_request->set_content_type( 'application/json; charset=utf-8' ).
lo_request->set_text( ld_payload ).
Now we can execute the request. The endpoint expects a POST request, so we set the POST when executing the EXECUTE method.
DATA(lo_response) = lo_client->execute( i_method = if_web_http_client=>post ).
Parse response
After we have executed the request, we should have received a response. This is in JSON format and must now be parsed back to ABAP.
To do this, we build a result structure so that we can easily parse the result.
TYPES: BEGIN OF ts_translation,
translatedtext TYPE string,
detectedsourcelanguage TYPE string,
END OF ts_translation.
TYPES tt_translation TYPE STANDARD TABLE OF ts_translation WITH EMPTY KEY.
TYPES: BEGIN OF ts_data,
translations TYPE tt_translation,
END OF ts_data.
TYPES: BEGIN OF ts_google_result,
data TYPE ts_data,
END OF ts_google_result.
To do this, we define a local structure with the target type. In the first step, we check whether the call was successful and then parse the result using DESERIALIZE.
DATA ls_google_result TYPE ts_google_result.
IF io_response->get_status( )-code = 200.
/ui2/cl_json=>deserialize( EXPORTING json = io_response->get_text( )
CHANGING data = ls_google_result ).
ENDIF.
Finally, we process the internal table of the structure and transfer the texts to our returning parameter. If there was an error before, the table is empty and it is not transferred, so we do not need any further error handling.
LOOP AT ls_google_result-data-translations INTO DATA(ls_translated).
INSERT ls_translated-TranslatedText INTO TABLE rt_result.
ENDLOOP.
Translate
So that our class can now be used easily, we implement two methods for translating. A simple method for translating individual texts (TRANSLATE_TEXT) and a method for translating many texts at once (TRANSLATE_TEXTS).
METHODS translate_text
IMPORTING id_text TYPE string
id_target_language TYPE string DEFAULT `en`
RETURNING VALUE(rd_result) TYPE string.
METHODS translate_texts
IMPORTING it_text TYPE string_table
id_target_language TYPE string DEFAULT `en`
RETURNING VALUE(rt_result) TYPE string_table.
The complete implementation of the methods and the entire example can be found in the section below.
Test
To test the API, we write appropriate ABAP unit tests. The implementation is done as a local test class in the test class include of our class.
CLASS ltc_google_api DEFINITION FINAL
FOR TESTING RISK LEVEL HARMLESS DURATION SHORT.
PRIVATE SECTION.
METHODS translate_apple FOR TESTING.
METHODS translate_fruits FOR TESTING.
METHODS dont_translate FOR TESTING.
ENDCLASS.
CLASS ltc_google_api IMPLEMENTATION.
METHOD translate_apple.
DATA(lo_cut) = NEW zcl_bs_demo_google_integration( ).
DATA(ld_result) = lo_cut->translate_text( `Apfel` ).
cl_abap_unit_assert=>assert_equals( exp = `Apple`
act = ld_result ).
ENDMETHOD.
METHOD translate_fruits.
DATA(lo_cut) = NEW zcl_bs_demo_google_integration( ).
DATA(lt_result) = lo_cut->translate_texts( VALUE #( ( `Apfel` ) ( `Birne` ) ) ).
cl_abap_unit_assert=>assert_equals( exp = VALUE string_table( ( `Apple` ) ( `Pear` ) )
act = lt_result ).
ENDMETHOD.
METHOD dont_translate.
DATA(lo_cut) = NEW zcl_bs_demo_google_integration( ).
DATA(ld_result) = lo_cut->translate_text( `Apple` ).
cl_abap_unit_assert=>assert_equals( exp = `Apple`
act = ld_result ).
ENDMETHOD.
ENDCLASS.
We test three different configurations: the simple method for one term, the method where we pass several words, and finally the method where we pass a text that has already been translated. In the last case, we assume that no changes are made to the word.
Complete example
Here you can find the complete example class with the two public methods for translating texts. Following best practices, we would actually create a global interface and a factory, but we have omitted this for the sake of clarity.
CLASS zcl_bs_demo_google_integration DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
"! Translate a single text
"! @parameter id_text | Text to be translated
"! @parameter id_target_language | Target language
"! @parameter rd_result | Translated text
METHODS translate_text
IMPORTING id_text TYPE string
id_target_language TYPE string DEFAULT `en`
RETURNING VALUE(rd_result) TYPE string.
"! Translates a table of texts into the target language
"! @parameter it_text | Table of texts
"! @parameter id_target_language | Target language
"! @parameter rt_result | Table of translated texts
METHODS translate_texts
IMPORTING it_text TYPE string_table
id_target_language TYPE string DEFAULT `en`
RETURNING VALUE(rt_result) TYPE string_table.
PRIVATE SECTION.
CONSTANTS c_api_endpoint TYPE string VALUE 'https://translation.googleapis.com/language/translate/v2'.
CONSTANTS c_api_key TYPE string VALUE ''.
TYPES: BEGIN OF ts_google_request,
q TYPE string_table,
target TYPE string,
END OF ts_google_request.
TYPES: BEGIN OF ts_translation,
translatedtext TYPE string,
detectedsourcelanguage TYPE string,
END OF ts_translation.
TYPES tt_translation TYPE STANDARD TABLE OF ts_translation WITH EMPTY KEY.
TYPES: BEGIN OF ts_data,
translations TYPE tt_translation,
END OF ts_data.
TYPES: BEGIN OF ts_google_result,
data TYPE ts_data,
END OF ts_google_result.
METHODS map_result
IMPORTING io_response TYPE REF TO if_web_http_response
RETURNING VALUE(rt_result) TYPE string_table
RAISING cx_web_message_error.
METHODS create_payload
IMPORTING it_text TYPE string_table
id_target_language TYPE string
RETURNING VALUE(rd_result) TYPE string.
METHODS create_url
RETURNING VALUE(rd_url) TYPE string.
ENDCLASS.
CLASS zcl_bs_demo_google_integration IMPLEMENTATION.
METHOD translate_text.
DATA(lt_result) = translate_texts( it_text = VALUE #( ( id_text ) )
id_target_language = id_target_language ).
IF line_exists( lt_result[ 1 ] ).
rd_result = lt_result[ 1 ].
ENDIF.
ENDMETHOD.
METHOD translate_texts.
DATA(ld_url) = create_url( ).
DATA(ld_payload) = create_payload( it_text = it_text
id_target_language = id_target_language ).
TRY.
DATA(lo_destination) = cl_http_destination_provider=>create_by_url( ld_url ).
DATA(lo_client) = cl_web_http_client_manager=>create_by_http_destination( lo_destination ).
DATA(lo_request) = lo_client->get_http_request( ).
lo_request->set_content_type( 'application/json; charset=utf-8' ).
lo_request->set_text( ld_payload ).
DATA(lo_response) = lo_client->execute( i_method = if_web_http_client=>post ).
rt_result = map_result( lo_response ).
CATCH cx_root.
CLEAR rt_result.
ENDTRY.
ENDMETHOD.
METHOD create_url.
rd_url = |{ c_api_endpoint }?key={ c_api_key }|.
ENDMETHOD.
METHOD create_payload.
DATA(ls_google_request) = VALUE ts_google_request( q = it_text
target = id_target_language ).
rd_result = /ui2/cl_json=>serialize( data = ls_google_request
name_mappings = VALUE #( ( abap = 'Q' json = 'q' )
( abap = 'TARGET' json = 'target' ) ) ).
ENDMETHOD.
METHOD map_result.
DATA ls_google_result TYPE ts_google_result.
IF io_response->get_status( )-code = 200.
/ui2/cl_json=>deserialize( EXPORTING json = io_response->get_text( )
CHANGING data = ls_google_result ).
ENDIF.
LOOP AT ls_google_result-data-translations INTO DATA(ls_translated).
INSERT ls_translated-TranslatedText INTO TABLE rt_result.
ENDLOOP.
ENDMETHOD.
ENDCLASS.
Conclusion
The Translate API is a fairly simple API and does not require an authentication flow, which means we can consume it very easily. We can now use the class anywhere in the system to translate texts.