Post

Exporting SAP Translations to XLIFF via Custom ABAP Class

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.