SAP itself has a report (and function modules) to import/export translations via XLIFF files. The transaction for the translation administration is LXE_MASTER. In this transaction you are able to create Object Lists (via tab Evaluations) which hold all relevant reports/DDIC objects for a translation project.

With the report RS_LXE_TEXTEXT_EXPORT you are able to export translations of such object lists into XLIFF files (which can be read by various localization tools - Weblate in our case). Exporting our pre-defined object lists with this report was just too laborious so we analyzed the code and developed a class which does basically the same - with the addition to be called from other ABAP code.

The core parts of this class are

  • reading of the object list
  • extracting the translations of every object into XLIFF

Reading the Object List

All relevant data about object lists is stored in the tables LXE_ATTOB (attributes) and LXE_COLOB (objects of the list). The first part is to get all relevant objects out of the list:

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.

" allow all types of short text
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.

" select all objects from the object lists
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.

Using this code we managed to get all relevant object for extracting the localization data.

Extract Localization Data

With the relevant objects from the object list we’re now able to extract the localization data:

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

Using these methods we’re able to export existing localization data of our SAP system to a XLIFF file.