How to implement a custom input component processor

e-Spirit AG

2020-02-20
Table of Contents

1. Introduction

This manual will show you how to implement a custom input component processor. Usually, such components will not be translated, because or their individual XML structure. Consequently, this custom XML has to be transformed into a readable structure and reconverted back into the original format to allow TranslationStudio to import the component again.

Every custom input component consists of the following parts:

  • A Converter to transform the 3rd party XML into a translatable XML structure.
  • A Restorer to convert the translatable XML into the original 3rd party XML format.
  • A configuration XML to add it to TranslationStudio.

You will find a Javadoc available at api/javadoc/txt-processor

2. Setup the projects

First, create a Java project, for example TXT - Your Component and add the following TranslationStudio Connector API JAR from your TranslationStudio package at /api/.

3. Implementation - Converter

Open the newly created project and create a new class

com.idmedia.translationstudio.specialist.txt.mycomponent.Converter implementing ITxtConverter

3.1. Create methods

The interface requires you to implement following methods:

/**
 * Create XML representation from Text content
 * @param sRawXml Raw XML as stored by the custom input component
 * @param sLangSrc Source Language
 * @param sLangDest Target Language
 * @param pTargetLanguageLocale Target Language Locale
 * @return
 */
@Nullable
public Element getXml(String sRawXml, String sLangSrc, String sLangDest, Locale pTargetLanguageLocale)
{
    // TODO: implement
    return null;
}

/**
 * Get the identifier to allow a restoration
 * @return
 */
@NotNull
public String getIdentifier()
{
    return "myidentifier"; // this has to match the identifier given in the txt processor xml
}

/**
 * Get the name of this converter
 * @return Name, must not be <code>null</code>
 */
@NotNull
public String getName()
{
    return "my converter";
}

3.2. Understanding the methods

The method public String getIdentifier(); will allow TranslationStudio to assign the converter’s XML with your custom converter and restorer.

The method public String getName(); is a name which will be logged.

The method public Element getXml(String sRawXml, String sLangSrc, String sLangDest, Locale pTargetLanguageLocale) will be called by TranslationStudio to receive a translatable XML from a raw XML parameter. You will also receive source and target language as well as target locale information. If this method returns null, the raw input XML will be imported 'as is' without any translation.

3.3. Choosing an XML structure

Generally, the XML provided by public Element getXml(String sRawXml, String sLangSrc, String sLangDest, Locale pTargetLanguageLocale) can have any structure. However, the enclosing translatable XML has certain standard tag names which you might want to use to mark content that is available for translation:

<TEXT>any translatable text</TEXT>
<DOM>translatable CMS_INPUT_DOM component</DOM>

For example, you may transform

<mycomponents-root-node any="attribute">
    <label>translatable text<label>
    <property name="some_property">
        <value>translatable text</value>
    </property>
</mycomponents-root-node>

to

<mycomponents-root-node any="attribute">
    <label>
        <TEXT>translatable text</TEXT>
    <label>
    <property name="some_property">
        <value>
            <TEXT>translatable text</TEXT>
        </value>
    </property>
</mycomponents-root-node>

3.4. Understanding the raw XML input

FirstSpirit will store a custom input component’s XML structure similar to the following example:

<txt>
    &amp;lt;?xml version="1.0" standalone="yes"?&amp;gt;
    &amp;lt;mycomponents-root-node any="attribute"&amp;gt;&amp;lt;label&amp;gt;&amp;lt;TEXT&amp;gt;translatable text&amp;lt;/TEXT&amp;gt;&amp;lt;label&amp;gt;&amp;lt;property name="some_property"&amp;gt;&amp;lt;value&amp;gt;&amp;lt;TEXT&amp;gt;translatable text&amp;lt;/TEXT&amp;gt;&amp;lt;/value&amp;gt;&amp;lt;/property&amp;gt;&amp;lt;/mycomponents-root-node&amp;gt;
</txt>

Your converter, however, will receive an clean XML ready to be parsed by any XML parser:

<?xml version="1.0" standalone="yes"?>
<mycomponents-root-node any="attribute"><label><TEXT>translatable text</TEXT><label><property name="some_property"><value><TEXT>translatable text</TEXT></value></property></mycomponents-root-node>

3.5. Understanding how your processed XML is stored

Once public Element getXml(String sRawXml, String sLangSrc, String sLangDest, Locale pTargetLanguageLocale) provides an Element node, it will appended to the parent txt node as is.

An attribute type will be added with the value of public String getIdentifier(), for example:

<txt type="myidentifier">
    <mycomponents-root-node any="attribute">
        <label>
            <TEXT>translatable text</TEXT>
        <label>
        <property name="some_property">
            <value>
                <TEXT>translatable text</TEXT>
            </value>
        </property>
    </mycomponents-root-node>
</txt>

4. Implementation - Restorer

After you have implemented the converter, create a new class com.idmedia.translationstudio.specialist.txt.mycomponent.Restorer implementing ITxtRestorer

4.1. Create methods

The interface requires you to implement the following methods:

/**
 * Restore XML
 *
 * @param pXml The XML created by {@link com.idmedia.translationstudio.api.specialists.txt.ITxtConverter}
 * @return String to be stored
 */
@NotNull
public String getXml(Node pXml);
{
    // do some magic
    return unprettyprint-restored-node;
}

/**
 * Get the name of this restorer
 * @return Name, must not be <code>null</code>
 */
@NotNull
public String getName()
{
    return "my restorer";
}

4.2. Understanding getXml

The method above will receive a node matching the XML structure provided by your converter. This method has to restore the original structure, for example:

<mycomponents-root-node any="attribute">
    <label>
        <TEXT>TRANSLATED TEXT</TEXT>
    <label>
    <property name="some_property">
        <value>
            <TEXT>TRANSLATED TEXT</TEXT>
        </value>
    </property>
</mycomponents-root-node>

to

<mycomponents-root-node any="attribute">
    <label>TRANSLATED TEXT<label>
    <property name="some_property">
        <value>TRANSLATED TEXT</value>
    </property>
</mycomponents-root-node>

You do not have to worry about XML escaping.

Importantly, your restored XML string should not have indentation. To achieve this, you may use the following method:

/**
 * Create a string representation of the given node. It will also
 * remove any indentation.
 *
 * @param pRestoredNode
 */
@NotNull
private String unprettyprintXml(@NotNull Node pRestoredNode)
{
    /**
     * Convert to string. Might be indented
     */
    final String sXml = NodePrintUtils.nodeToString(pRestoredNode, false, true);

    /**
     * Basically remove all whitespace between > < which only matches formatting
     * indentations.
     *
     * Thereafter, out-dent all new line textarea lines
     */
    return sXml.replaceAll(">\\s{1,}<","><").replaceAll("\n {2,}", "\n");
}

5. Adding the processor to TranslationStudio

Adding your converter and restorer to TranslationStudio consists of the following steps:

_ Stop the TranslationStudio application. _ Deploy the JAR file to TranslationStudio’s lib directory. _ Update TranslationStudio’s conf/processors-txt.xml file. _ Start the TranslationStudio application.

The xml file contains a root node instances. To add a new custom processor, add the required configuration to the root node, for example:

<?xml version="1.0" encoding="UTF-8"?>
<instances>
    <instance>
        <convert>com.idmedia.translationstudio.specialist.txt.mycomponent.Converter</convert>
        <restore>com.idmedia.translationstudio.specialist.txt.mycomponent.Restorer</restore>
        <identifier>myidentifier</identifier>
        <expect-start-content-page>&amp;lt;mycomponents</expect-start-content-page>
        <expect-start-content-entity>&amp;amp;lt;mycomponents</expect-start-content-entity>
        <expect-content>
            <expect>any=</expect>
        </expect-content>
    </instance>
</instances>

The identifier node has to match the value of getIdentifier().

The two nodes

  • expect-start-content-page
  • expect-start-content-entity

show TranslationStudio how to identify that a given XML has to be processed using your custom processor. This decision will be made upon a starts-with match.

Optionally, you can add additional expected content by adding additional <expect>value</expect> nodes to the expect-content node.

This expect-content node may also be empty!

6. Final words

Congratulations, you have successfully created your own custom input component converter and restorer.

If you need further information, please do not hesitate open a support ticket.