Text Attributes

Summary

Text attributes are used for storing texts in multiple languages. Text attributes are semantically single-valued, meaning that each attribute instance contains logically only one text, but this text in as many translations as appropriate.

Concepts & Terms

Locale

A locale is an identifier for a language. It consists of two parts, one identifying the language (e.g. �en� for English, �de� for German: lower-case two-letter code as defined by ISO-639, see http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt), the other specifying the country and its local dependencies (e.g. (en,US) for �American English�, (de,CH) for �Swiss German�: upper-case two-letter codes as defined by ISO-3166, see http://www.chemie.fu-berlin.de/diverse/doc/ISO_3166.html). Text attributes do not support variants.

Original Locale

The original locale identifies the language from which the translation process starts. It can be set for Units with the method IUnit.setOriginalLocale(Locale locale). A text attribute inherits the original locale from the unit in which it is persisted. Main reason for this is that the translation process is based on the unit granularity, and requires that all text attributes within one unit have the same original locale.

Please see also the section below on the inheritance mechanism of deltalinks where interesting side-effects are described.

Default Locale

The default locale identifies the default language in the Pcd. It serves as first fallback language if the text is not available for the requested locale. The default locale can be configured in the PCD configuration using the properties Pcd.Texts.DefaultLocale_Language and Pcd.Texts.DefaultLocale_Country.

Empty Locale

In general, it cannot be guaranteed that an original locale is assigned to really every unit. Customers e.g. who don't have a translation process set up for their own objects might not want to specify an original locale.

Also the default locale of the system need not be specified.

But even then it is better (in mot cases) to return an existing text from another locale than nothing!

As a last resort for the fallback rules, the Empty Locale is used. The empty locale is defined by IPcdAttribute.EMPTY_LOCALE. Every text attribute that is not empty contains always an Empty Locale text mapping!

Use Original Locale Only Modus

This operational mode of the Pcd is used primarily in systems where content is being developed. If it is active, the methods that store and read texts will ignore the given Locale parameter and only use the empty locale. A subsequent translation process is expected to add the texts in all required real locales.

In order to activate the Original Locale Only modus set the parameter Pcd.Texts.UseOriginalLocaleOnly = true in the PCD configuration.

Text Type

The text type describes for each text attribute in which user interface context a text is used, e.g. title or bullet list item text. This information is mandatory for the SAP-internal translation process. The text type can be set by using the method IPcdAttribute.setTextType(String textType).

Scenarios

Creating Text Attributes

A text attribute can be created by the method IPcdUtils.createPcdAttribute(PcdAttributeValueType type, String attributeName) where type is PcdAttributeValueType.TEXT. The text type can be set by using IPcdAttribute.setTextType(String textType).

For setting texts the method IPcdAttribute.set(Locale locale, String attrVal) is used. The method assigns the given String to the specified locale (except if the Use Original Locale Only Modus is switched on) .Since all text attributes must have an empty locale assignment the first text is stored for the empty locale as well. Nevertheless it is possible to set the text for the empty locale explicitly.

Modifying Text Attributes

Persisted text attributes can be modified using the IPcdContext.modifyAttributes() methods. These methods take as parameters a modification operation and a attribute. The modification operation is either DirContext.REPLACE_ATTRIBUTE, DirContext.ADD_ATTRIBUTE, or DirContext.REMOVE_ATTRIBUTE.

Here's what happens in the persistency depending on the given modification operation.

DirContext.REPLACE_ATTRIBUTE:

The incoming attribute completely replaces the existing attribute, if any.

DirContext.REMOVE_ATTRIBUTE:

If the incoming text attribute has values then the texts for the specified locales are removed in the persistency. However, the text of the Empty Locale is not removed.

If the incoming attribute has no values, then the attribute is removed completely.

DirContext.ADD_ATTRIBUTE:

The texts in the incoming attribute are added to the persisted attribute. Texts of already existing locales are replaced! This is different compared to non-text attributes.

It is distinguished whether the empty locale text of the incoming attribute was set explicitly, or implicitly when storing the first text value to the attribute instance. If the empty locale text was set explicitly, then it replaces the old text. If the empty locale text was not set explicitly, then the persisted empty locale text remains unchanged.

Reading Text Attributes: implicit usage of the fallback rules.

Texts can be read using the IPcdAttribute.get(Locale locale) method. 

If the Use Original Locale Only modus is switched on then always the text of the Empty Locale is returned.

If the Use Original Locale Only modus is switched off then the text of the requested locale is returned if existent. If the text does not exist then the following fallback strategy is used:

Deltalinks

Deltalinks expose the attributes and the sub-hierarchy that they inherit from their target unit. Through regular usage of the API, changes can be applied to this inherited tree. The changes that are done to the inherited attributes and tree are stored as modification operation within the deltalink object so that they can be re-applied later when the deltalink is read from the persistency again (for example after a system restart).

This means that the rules that were described above for the modifyAttributes method also apply within a deltalink.

This is important to understand when one looks at the translation of such persisted modifications. The modifications are stored within the deltalink, and thus have the same original locale as the deltalink itself. Inherited attributes keep their original locale (which is the original locale in which they are persisted).

So it can happen that different text attributes on a single node in the Pcd have different original locales.

If the deltalink stores "add" modifications, then the original locale of the "visible" attribute remains unchanged (as inherited).

If the deltalink stores "replace" modifications, then the inherited attribute is discarded and the "visible" attribute is the one that is stored in the deltalink.

 

Editors

What modification operation should a tool use for text attributes if it wants to support the translation process in the best possible way? There are different scenarios:

In-house development with subsequent translation process:

Use Original Locale Only Modus = true. The users maintain in original locale only, texts are stored in empty locale only, a subsequent translation process provides the translations. "Replace" is the right modification operation for text attributes!

Customer development system without external translation process:

If the user wants to replace an existing text with a semantically new one, then all translations should be discarded. In this case again "Replace" is the appropriate modification operation.

If the user just wants to add a new text, or replace an existing translation in a certain language, then they do not want to interfere with other existing languages: "add" is the right thing to do.

So the user should be able to decide which modification operation to use!

 

Example

Source code of an example method

static void demoTexts(IPcdUtils pcdUtils, IPcdContext context) throws NamingException
{
    System.out.println("The original locale of the unit is:");
    IUnit unit = context.getUnit("");
    System.out.println(" language = " + unit.getOriginalLanguage());
    System.out.println(" country = " + unit.getOriginalCountry());

    System.out.println();
    System.out.println("Create a text and store it at the context");

    IPcdAttribute carTextAttr =
        pcdUtils.createPcdAttribute(
        PcdAttributeValueType.TEXT,
        "textid_car"
);
    carTextAttr.setTextType("XBLI");
    carTextAttr.set(Locale.US, "Car");
    printTextAttribute(carTextAttr);

    Attributes attrs = pcdUtils.createPcdAttributes();
    attrs.put(carTextAttr);

    context.modifyAttributes("", DirContext.REPLACE_ATTRIBUTE, attrs);

    System.out.println();
    System.out.println("Read the text from the context after the modification");

    Attributes readAttrs = context.getAttributes("");
    printTextAttribute(readAttrs.get("textid_car"));

    System.out.println();
    System.out.println("Translate the text and store the translated text at the context");
    IPcdAttribute carTextAttrTranslated = 
        pcdUtils.createPcdAttribute(PcdAttributeValueType.TEXT, "textid_car");
    carTextAttrTranslated.set(Locale.US, "car");
    carTextAttrTranslated.set(Locale.GERMAN, "Auto");
    carTextAttrTranslated.set(new Locale("es", ""), "coche");

    printTextAttribute(carTextAttrTranslated);

    Attributes attrsTranslated = pcdUtils.createPcdAttributes();
    attrsTranslated.put(carTextAttrTranslated);
    context.modifyAttributes("", DirContext.ADD_ATTRIBUTE, attrsTranslated);

    System.out.println();
    System.out.println("Read the translated text from the context after the modification");

    Attributes readTranslatedAttrs = context.getAttributes("");
    IPcdAttribute readTranslatedAttr = (IPcdAttribute) readTranslatedAttrs.get("textid_car");
    printTextAttribute(readTranslatedAttr);

    System.out.println();
    System.out.println("Read the text for different locales:");
    System.out.println(" Spanish: " + readTranslatedAttr.get(new Locale("es", "")));
    System.out.println(" Spanish/Argentina: " + readTranslatedAttr.get(new Locale("es", "AR")));
    System.out.println(" Chinese: " + readTranslatedAttr.get(Locale.CHINESE));
}

private static void printTextAttribute(Attribute attr)
{
    System.out.println("Texts of attribute '" + attr.getID() + "':");
    try
    {
        Enumeration enum = attr.getAll();
        while (enum.hasMoreElements())
        {
        Object obj = enum.nextElement();
        PlLocaleTextPair ltp = (PlLocaleTextPair) obj;
        System.out.println(" " + ltp.toString());
        }
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }
}

Output of the example method

The original locale of the unit is:
language = en
country = US

Create a text and store it at the context
Texts of attribute 'textid_car':
Locale = en_US; Text = Car
Locale = ; Text = Car

Read the text from the context after the modification
Texts of attribute 'textid_car':
Locale = en_US; Text = Car
Locale = ; Text = Car

Translate the text and store the translated text at the context
Texts of attribute 'textid_car':
Locale = en_US; Text = car
Locale = es; Text = coche
Locale = de; Text = Auto
Locale = ; Text = car

Read the translated text from the context after the modification
Texts of attribute 'textid_car':
Locale = en_US; Text = car
Locale = es; Text = coche
Locale = de; Text = Auto
Locale = ; Text = Car

Read the text for different locales:
Spanish: coche
Spanish/Argentina: coche
Chinese: car