This is a test message to test the length of the message box.
Login
BTP Google Translate API
Erstellt von Software-Heroes

BTP - Google Translate Integration

59

Wie rufst du aus dem ABAP Environment eine externe API, wie Google Translate, auf und konsumierst diese in einer lokalen Klasse? Mehr dazu in diesem Artikel.

Werbung


Bereits in einem älteren Artikel haben wir uns die Provisionierung der Google Translate API angeschaut und wie du sie aufrufen kannst.

 

Einleitung

Die BTP stellt zahlreiche Services und Dienste zur Verfügung, doch wie sieht es mit der Integration von anderen Diensten aus? Die Einbindung von REST-basierten Diensten kann relativ leicht über die neuen HTTP Klassen im System erfolgen. Die Translate API benötigt dazu nur einen Aufruf und sendet uns die übersetzten Token zurück.

 

Implementierung

In diesem Abschnitt wollen wir den API Aufruf implementieren und erklären dir alles Schritt für Schritt.

 

URL

Im ersten Schritt benötigen wir die URL der API mit dem API Key, damit wir den Service ohne Authentifizierung verwenden können. Dazu legen wir uns Global zwei Konstanten an.

CONSTANTS c_api_endpoint TYPE string VALUE 'https://translation.googleapis.com/language/translate/v2'.
CONSTANTS c_api_key      TYPE string VALUE ''.

 

Per String-Template bauen wir uns dann die finale URL zusammen, nach dem Endpunkt kommt der Parameter mit dem API Schlüssel.

rd_url = |{ c_api_endpoint }?key={ c_api_key }|.

 

Payload

Im nächsten Schritt generieren wir die Payload, hierbei handelt es sich um die Anfrage an Google Translate. Dazu definieren wir einen Typen für die Struktur. Den Aufbau der Anfrage kannst du aus der API Dokumentation entnehmen.

TYPES: BEGIN OF ts_google_request,
         q      TYPE string_table,
         target TYPE string,
       END OF ts_google_request.

 

Dazu übergeben wir die zu übersetzenden Texte und die Zielsprache an die Struktur und erzeugen das JSON für die Anfrage. Wir rufen die SERIALIZE Methode auf und übergeben für die Felder ein Mapping, um die korrekten kleingeschriebenen Feldnamen zu erhalten.

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' ) ) ).    

 

Die Payload sollte nun ungefähr so aussehen:

{
  "q": [
    "Test Artikel", 
    "Bitte diesen Text ins Englische übersetzen."
  ],
  "target": "en"
}

 

Anfrage

Nun wollen wir die Anfrage erzeugen, dazu müssen wir im ersten Schritt mit der URL eine Destination erzeugen und dann über den Client-Manager ein Client Objekt.

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 ).

 

Wir lassen uns vom Client eine Anfrage geben und konfigurieren diese, indem wir den ContentType setzen und die Payload mitgeben.

DATA(lo_request) = lo_client->get_http_request( ).
lo_request->set_content_type( 'application/json; charset=utf-8' ).
lo_request->set_text( ld_payload ).

 

Nun können wir die Anfrage ausführen. Der Endpunkt erwartet einen POST-Request, deshalb setzen wir beim Ausführen der EXECUTE Methode noch den POST.

DATA(lo_response) = lo_client->execute( i_method = if_web_http_client=>post ).

 

Antwort parsen

Nachdem wir die Anfrage ausgeführt haben, sollten wir eine Antwort erhalten haben. Diese ist im JSON Format und muss nun zurück nach ABAP geparst werden.

 

Dazu bauen wir uns eine Ergebnisstruktur auf, um das Ergebnis leicht parsen zu können.

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.

 

Dazu definieren wir uns eine lokale Struktur mit dem Zieltypen. Im ersten Schritt prüfen wir, ob der Aufruf erfolgreich war und parsen dann das Ergebnis per 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.

 

Zum Abschluss verarbeiten wir die interne Tabelle der Struktur und übernehmen die Texte in unseren Returning Parameter. Gab es zuvor einen Fehler, ist die Tabelle leer und es wird nicht übernommen, damit benötigen wir keine weitere Fehlerbehandlung.

LOOP AT ls_google_result-data-translations INTO DATA(ls_translated).
  INSERT ls_translated-TranslatedText INTO TABLE rt_result.
ENDLOOP.

 

Übersetzen

Damit unsere Klasse nun einfach verwendet werden kann, implementieren wir zwei Methoden zum Übersetzen. Eine einfache Methode um einzelne Texte zu übersetzen (TRANSLATE_TEXT) und eine Methode um viele Texte auf einmal zu übersetzen (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.

 

Die vollständige Implementierung der Methoden und des gesamten Beispiels findest du im Abschnitt unten.

 

Test

Um die API zu testen, schreiben wir entsprechende ABAP Unit Tests. Die Implementierung erfolgt als lokale Testklasse im Testklassen-Include unserer Klasse.

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.

 

Wir testen drei verschiedene Konstellationen, einmal die einfache Methode für einen Begriff, einmal übergeben wir mehrere Wörter und zum Abschluss übergeben wir einen bereits übersetzten Text. Im letzten Fall gehen wir davon aus, dass keine Änderung am Wort vorgenommen wird.

 

Vollständiges Beispiel

Hier findest du die vollständige Beispielklasse mit den beiden öffentlichen Methoden zum Übersetzen von Texten. Der Best Practices folgend, würden wir eigentlich noch ein globales Interface und eine Factory erzeugen, dies haben wir uns zur besseren Übersicht gespart.

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.

 

Fazit

Die Translate API ist eine recht einfache API und benötigt keinen Autentifizierungsflow, wodurch wir sie sehr leicht konsumieren können. Die Klasse können wir nun überall im System verwenden, um Texte zu übersetzen.


Enthaltene Themen:
BTPABAP EnvironmentGoogle TranslateHTTP
Kommentare (0)



Und weiter ...

Bist du zufrieden mit dem Inhalt des Artikels? Wir posten jeden Freitag neuen Content im Bereich ABAP und unregelmäßig in allen anderen Bereichen. Schaue bei unseren Tools und Apps vorbei, diese stellen wir kostenlos zur Verfügung.


BTP - Inbound und Outbound Kommunikation

Kategorie - ABAP

In diesem Artikel gehen wir im Detail auf die Anbindung des SAP BTP ABAP Environment ein und wie die einzelnen Verbindungen erzeugt werden.

04.10.2024

BTP - Transport ZLOCAL Objekte

Kategorie - ABAP

Du möchtest deinen PoC im ABAP Environment nun auf das nächste System bringen? In diesem Artikel ein paar mehr Informationen.

01.10.2024

RAP - Übersetzungs-App (Beispiel)

Kategorie - ABAP

Schauen wir uns einmal ein praktisches Beispiel für die Entwicklung einer RAP Anwendung im ABAP Environment an und wie du mit wenig Aufwand eine App erstellst.

02.08.2024

BTP - Mehrere Communication Systems

Kategorie - ABAP

Wie können wir verschieden Verbindungen im gleichen Communication Scenario unterscheiden und die richtige für uns ableiten? Mehr dazu hier.

23.07.2024

RAP - Custom Entity Wertehilfe (Deep Dive)

Kategorie - ABAP

Mit der Custom Entity hast du in RAP die meisten Freiheiten in der Entwicklung von ABAP Cloud Anwendungen, doch wie sieht es mit potentiellen Fehlern aus?

12.07.2024