Post

Exporting SAP Translations to XLIFF via Custom ABAP Class

Learn how to export SAP translations to XLIFF using a custom ABAP class. This guide covers automation, integration with localization tools, and best practices for managing SAP translation data.

Exporting SAP Translations to XLIFF via Custom ABAP Class

Working with localization in SAP systems can be quite the task — especially when your team needs to extract translations for use in external tools like Weblate or other XLIFF-compatible editors. While SAP provides tooling (such as the transaction LXE_MASTER and report RS_LXE_TEXTEXT_EXPORT), the manual handling can be tedious and lacks flexibility.

In this post, I’ll walk you through how we tackled this issue by creating a custom ABAP class that reads predefined object lists and exports the data into XLIFF — in a way that’s reusable across other ABAP logic.

Along the way, we’ll explore some important SAP translation infrastructure concepts and walk through the main building blocks of the solution.


🧠 Understanding XLIFF and SAP’s Translation Layer

Before diving into code, a quick overview:

  • XLIFF (XML Localization Interchange File Format) is an industry-standard XML format for storing translatable data and their translations.
  • In SAP, translations are managed via the Translation Hub, and objects to be translated are grouped in Object Lists within LXE_MASTER.
  • These object lists can include reports, data elements, domains, etc.

Exporting translations in XLIFF format enables integration with modern localization pipelines, including services like Weblate or Crowdin.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
DATA lt_objtypes TYPE TABLE OF lxeobjtype.

" check if there is at least one object list
IF gr_objlist IS INITIAL.
  RAISE EXCEPTION TYPE cx_lxe_textext
    EXPORTING
      textid = cx_lxe_textext=>object_selection.
ENDIF.

" Read allowed object types
SELECT obj_type
  FROM lxe_attob
  INTO TABLE lt_objtypes
  WHERE typeatt EQ cl_lxe_constants=>c_typeatt_short.
IF sy-subrc NE 0.
  RAISE EXCEPTION TYPE cx_lxe_textext
    EXPORTING
      textid = cx_lxe_textext=>object_selection.
ENDIF.

" allow export of virtual objects
APPEND cl_lxe_constants=>c_objtype_dems
  TO lt_objtypes.

" Get translation objects
SELECT custmnr objtype objname orig_lang collnam domanam
  FROM lxe_colob
  INTO CORRESPONDING FIELDS OF TABLE gt_colob_st
  FOR ALL ENTRIES IN lt_objtypes
    WHERE objlist IN gr_objlist
      AND objtype EQ lt_objtypes-table_line.

gr_objlist contains the object lists you want to export.


🌍 Extracting and Formatting Localization Data

After retrieving the objects, the next step is converting them into XLIFF. This includes reading existing translations, formatting the XML structure, and transforming it into a human-readable and standards-compliant format.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
METHOD create_xliff_for_lang.

  DATA: lv_source_lang      TYPE lxestring,
        lv_destination_lang TYPE lxestring,
        lv_reference_lang   TYPE lxestring,
        lv_tstamp_str       TYPE lxestring.

  DATA: lt_objects TYPE cl_lxe_textext_aux=>tt_lxe_object_full.

  " prepare the texts
  lt_objects = prepare_short_texts( iv_lang ).

  " convert langs to xml
  lv_source_lang = cl_lxe_textext_aux=>convert_lang_sap_to_xml( gv_source_lang ).
  lv_destination_lang = cl_lxe_textext_aux=>convert_lang_sap_to_xml( iv_lang ).
  lv_tstamp_str = create_xliff_date( ).

  CALL TRANSFORMATION lxe_shorttext_to_xliff
    SOURCE objects      = lt_objects
            src_lang     = lv_source_lang
            tgt_lang     = lv_destination_lang
            ref_lang     = lv_reference_lang
            timestamp    = lv_tstamp_str
    RESULT XML rv_result.

  " Transform it to a nicely intended XML
  TRY.
      " read the XML file
      DATA(lo_reader) = cl_sxml_string_reader=>create( rv_result ).

      " set up the writer
      DATA(lo_writer) = CAST if_sxml_writer( cl_sxml_string_writer=>create( type     = if_sxml=>co_xt_xml10
                                                                            encoding = 'UTF-8' ) ).
      lo_writer->set_option( if_sxml_writer=>co_opt_linebreaks ).
      lo_writer->set_option( if_sxml_writer=>co_opt_indent ).

      lo_reader->next_node( ).
      lo_reader->skip_node( lo_writer ).

      " and write it back to the xstring
      rv_result = CAST cl_sxml_string_writer( lo_writer )->get_output( ).
    CATCH cx_root INTO DATA(e_txt).
  ENDTRY.

  " validate the created xliff
  cl_lxe_textext_aux=>validate_xliff(
    EXPORTING
      xliff   = rv_result
    EXCEPTIONS
      invalid = 1 ).
  IF sy-subrc NE 0.
    RAISE EXCEPTION TYPE cx_lxe_textext
      EXPORTING
        textid = cx_lxe_textext=>xml_transform.
  ENDIF.

ENDMETHOD.

*Pretty-printing XML: The code includes logic to reformat the generated XLIFF to make it readable using cl_sxml_string_reader and cl_sxml_string_writer.


🏗️ Building the Text Export Logic

🧱 Prepare Translation Objects

The prepare_short_texts method reads the translation data using SAP’s standard function modules like LXE_OBJ_TEXT_PAIR_READ.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
METHOD prepare_short_texts.

  DATA: ls_object TYPE lxe_object_full,
        ls_texts  TYPE lxe_texts_export.

  DATA: lt_pcx_s1 TYPE TABLE OF lxe_pcx_s1.

  FIELD-SYMBOLS: <colob> TYPE t_colob,
                  <pcx>   TYPE lxe_pcx_s1.

  " get the objects
  LOOP AT gt_colob_st ASSIGNING <colob>.
    CLEAR: ls_object, lt_pcx_s1.

    MOVE-CORRESPONDING <colob> TO ls_object.

    IF ls_object-objname(3) = '---'.
      ls_object-objname(3) = sy-mandt.
    ENDIF.

    CALL FUNCTION 'LXE_OBJ_TEXT_PAIR_READ'
      EXPORTING
        t_lang    = iv_lang
        s_lang    = gv_source_lang
        custmnr   = ls_object-custmnr
        objtype   = ls_object-objtype
        objname   = ls_object-objname
      TABLES
        lt_pcx_s1 = lt_pcx_s1.

    CHECK lt_pcx_s1 IS NOT INITIAL.

    CALL FUNCTION 'LXE_OBJ_TRANSLATION_STATUS2'
      EXPORTING
        t_lang  = iv_lang
        s_lang  = gv_source_lang
        custmnr = ls_object-custmnr
        objtype = ls_object-objtype
        objname = ls_object-objname
      IMPORTING
        stattrn = ls_object-stattrn.

    " cumulate
    " (target text must by ignored as it would cause redundant translations)
    SORT lt_pcx_s1 BY s_text unitmlt.
    DELETE ADJACENT DUPLICATES FROM lt_pcx_s1 COMPARING s_text unitmlt.

    LOOP AT lt_pcx_s1 ASSIGNING <pcx>.
      MOVE-CORRESPONDING <pcx> TO ls_texts.
      APPEND ls_texts TO ls_object-texts.
    ENDLOOP.

    prepare_short_texts_lines( EXPORTING iv_lang   = iv_lang
                                CHANGING cs_object = ls_object ).

    ls_object-path = |//{ sy-sysid }//{ sy-mandt }//{ ls_object-custmnr }//{ ls_object-objtype }//{ ls_object-objname }|.

    APPEND ls_object
      TO rt_objects.
  ENDLOOP.

ENDMETHOD.

Redundant entries are cleaned, and a reference path is constructed for each object.


📐 Prepare Short Text Lines

This step evaluates the translation state (e.g., translated/untranslated) and marks the approved status accordingly.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
METHOD prepare_short_texts_lines.

  CONSTANTS: lc_approved_yes TYPE string VALUE 'yes',
              lc_approved_no  TYPE string VALUE 'no'.

  DATA: lv_stattrn   TYPE lxestattrn,
        lt_ref_texts TYPE STANDARD TABLE OF lxe_pcx_s1,
        wa_text      TYPE lxe_pcx_s1.

  FIELD-SYMBOLS <text> TYPE lxe_texts_export.

  LOOP AT cs_object-texts ASSIGNING <text>.
    CLEAR lv_stattrn. " lv_pstatus.

    TRY.
        cl_lxe_pp=>factory->getlib_eval( )->evaluate_get(
          EXPORTING
            i_src_lang      = gv_source_lang
            i_tgt_lang      = iv_lang
            i_domain        = cs_object-domanam
            i_src_text      = <text>-s_text
            i_tgt_text      = <text>-t_text
            i_max_length    = <text>-unitmlt
          IMPORTING
            e_trl_status    = lv_stattrn
            e_best_p_text   = <text>-best_pp
            e_best_p_status = <text>-best_pp_status ).
      CATCH cx_lxe_pp.
        DELETE TABLE cs_object-texts FROM <text>.
        CONTINUE.
    ENDTRY.

    IF lv_stattrn = cl_lxe_constants=>c_trl_status_translated.
      <text>-state = lv_stattrn.
      <text>-approved = lc_approved_yes.
    ELSE.
      <text>-state = lv_stattrn.
      <text>-approved = lc_approved_no.
    ENDIF.

  ENDLOOP.

ENDMETHOD.

Depending on e_trl_status, the translation line is either marked approved (yes) or not (no).


🛠️ Use Cases and Integration

Once the class is complete, it can be reused from other ABAP reports or batch jobs, for example:

  • Scheduled background jobs to export translations nightly.
  • Integration with CI/CD pipelines to push localization content.
  • Syncing translations with external translators using Git + Weblate.

🧠 Final Thoughts

Adding automation around SAP’s translation tools can save a ton of time, especially for internationalized systems that change frequently. By wrapping RS_LXE_TEXTEXT_EXPORT logic into a callable class, we made our translation export process scriptable and reliable.

XLIFF as a format opens the door to powerful external tooling and allows us to keep translations versioned alongside our codebase.

This post is licensed under CC BY 4.0 by the author.