1. Introduction

This manual will show you how to implement an extended version of the generic filesystem connector available to all TranslationStudio modules.

The connector will allow you to add any number of projects to the connector with their own settings:

  • Job configuration to determine when to start a translation for all pages/datasets using the respective project.

  • A notification email address.

  • Target folder that will contain copies of the translatable xml files.

  • A command that might be executed after the translatable xml files have been created.

Each connector consists of 2 projects:

  1. FirstSpirit module to provide the configuration panel.

  2. TranslationStudio JAR library with the connector implementation.

This example connector implementation will assume Netbeans as your IDE.

You will find a Javadoc available at api/javadoc/connector

2. Setup the projects

First, create a FirstSpirit module maven project CONNECTOR - Example - Frontend. You will have to add the TranslationStudio FSM’s JAR file as dependency to obtain the API necessary to implement this FirstSpirit module.

As part of the FirstSpirit module, you have to add a module-isolated.xml file. TranslationStudio uses an auto connector lookup by checking all available modules. Each module, whose public component’s name starts with the prefix TSConnector will be considered a TranslationStudio connector and made available to TranslationStudio.

<module>
    <name>myconnector</name>
    <displayname>FirstSpirit TranslationStudio Connector (I,L)</displayname>
    <version>${project.version}</version>
    <vendor>${vendor}</vendor>
    <description>FS5.2 Module ${name}</description>
    <components>
        <public>
            <name>TSConnector myconnector</name>
            <displayname>myconnector</displayname>
            <class>com.idmedia.translationstudio.connector.example.configuration.Connector</class>
            <resources>
                <resource scope="module" name="${project.groupId}:${project.artifactId}" version="${project.version}" minVersion="${project.version}" mode="isolated">lib/${project.artifactId}-${project.version}.jar</resource>
            </resources>
        </public>
    </components>
</module>

Second, create a Connector maven project CONNECTOR - Example - Backend.The POM files will require you to add the following dependencies to your projects:

  1. FirstSpirit access JAR (from your FirstSpirit installation).

  2. TranslationStudio Connector API jar (from your TranslationStudio ./lib folder).

3. Implementation - CONNECTOR - Example - Frontend

Open the newly created project CONNECTOR - Example - Frontend and create a new class Connector that implements com.idmedia.translationstudio.api.connector.gui.ITranslationStudioConnector

@Override
public String getInstanceClass()
{
    /** this is the full qualified class name of the actual backend
        connector implementation. A new instance will be created using
        java reflections. */

    return "com.idmedia.translationstudio.testautomation.connector.Connector";
}

@Override
public ITranslatioStudioConnectorGui getGui()
{
    return new TranslatioStudioConnectorGuiImpl();
}

Now, create a new class TranslatioStudioConnectorGuiImpl which implements com.idmedia.translationstudio.api.connector.gui.ITranslatioStudioConnectorGui. This

The actual connector configuration panel can be provided by creating a new JPanel class and providing this via the method public JPanel getConfigurationPanel().

3.1. Understanding the configuration

A connector basically contains 2 types of configuration:

  1. The configuration necessary to restore the conplete configuration panel (including some labels, displaynames etc.)

  2. The configuration required by the connector implementation to perform its tasks (usually, this configuration is much smaller than the connector configuration panel’s configuration).

@Override
public String getGuiConfiguration()
{
    /** this data will only be available to the FSM */
    return "";
}

@Override
public String getConnectorConfiguration()
{
    /** this data will be transferred to your backend connector implementation 'as is'. */
    return "";
}

3.2. Initialising the panel

Since this panel will now be accessible, we need to add a method to initialise it once it has been created.

Therefore, add the following method and ignore potential errors for the moment:

/**
 * Init the panel right after it is being displayed
 * @param sId The ID of this connector provided by TranslationStudio. Do not change this!
 * @param sConfiguration Configuration as provided by onSaveGuiConfiguration()
 * @param vpQuotas List of available quota configurations (always has at least 1 entry, the "default" quota)
 * @param pCommunicator Object to send custom messages to your backend connector instance
 */
public void onInit(String sId, String sConfiguration, List<IProjectJobConfigurtion> vpQuotas, IExchangeCommunicator pCommunicator)
{
    /** init your JPanel based on the configuration data provided by sXml */
}

3.3. Creating a list of configured projects

Finally, we have to provide a list of configured projects ready to be used by TranslationStudio and in your language mappings in your FirstSpirit project.

You will need to provide at least 1 valid object for the connector to be available.

Therefore, add the following method:

/**
 * Create a list of configured projects that will be used by TranslationStudio and eventually made available
 * to the language mappings dropdown component.
 * @return
 */
@NotNull
List<IConfiguredTranslationMemoryProject> getConfiguredProjects()
{
    final List<IConfiguredTranslationMemoryProject> vpList = new ArrayList<>();

    /*
     * @TODO: Provide at least 1 configured project to use this connector.
     */

    return vpList;
}

4. Implementation - CONNECTOR - Example - Backend

Open the newly created project CONNECTOR - Example - Backend.

This project will provide the business logic of the connector.

You can access the entire TranslationStudio API via TranslationStudio.get() at any time.

Create a new class com.idmedia.translationstudio.connector.example.impl.Connector implementing ITranslationMemoryConnector

This class has to match your return value as given in the FSMs public String getInstanceClass().

A connector has three tasks to perform:

/**
 * Operation to transfer files into the TMS
 * @return
 */
public ITranslationOperation getTranslationOperation();

/**
 * Operation to query for finished translations
 * @return
 */
public ITranslationStatusQueryOperation getTranslationStatusQueryOperation();

/**
 * Operation to manage queries from the connector's configuration panel (ServerManager)
 * @param sQueryData Input data
 * @return
 */
public IQueryOperation getQueryOperation(String sQueryData);

Each of the first two operations will be requested from TranslationStudio after the setup method has been called:

/**
 * Setup the connector
 *
 * @param sTmsId Tms Id
 * @param sConfigurationXml Connector Configuration (as provided by {@link com.idmedia.translationstudio.api.connector.gui.ITranslationMemorySystemConnectorConfigurationGui.getConnectorConfiguration}
 * @param sWorkingDirectory Connector Working Directory
 * @param pContext Connector Context
 * @return
 */
public boolean setup(String sTmsId, String sConfigurationXml, String sWorkingDirectory, IConnectorContext pContext);

There are 2 essential parameters:

  1. sConfigurationXml
    This parameter provides the exact String provided by the connector configuration implementation of ITranslationMemorySystemConnectorConfigurationGui.getConnectorConfiguration()

  2. pContext
    This parameter provides an access point to the TranslationStudio API

In addition, you may react to any changes to your connector configuration (i.e. whenever the configuration has been updated using TranslationStudio’s configuration panel):

/**
 * Method called when the settings are updated using the ServerManager
 * @param sConfigurationXml Connector Configuration (as provided by {@link com.idmedia.translationstudio.api.connector.gui.ITranslationMemorySystemConnectorConfigurationGui.getConnectorConfiguration}
 */
public void onUpdateConfiguration(String sConfigurationXml);

4.1. Setting it all up

Your implementation of the setup method has to evaluate the xml provided by the connector configuration panel.

Since this is simple XML evaluation, I leave this up to you. However, you have to store the result in a member variable, for example:

/**
 * Hold the configuration
 */
private final Configuration m_pConfiguration = new Configuration();

/**
 * {@inheritDoc }
 */
@Override
public boolean setup(String sTmsId, String sConfigurationXml, String sWorkingDirectory, IConnectorContext pContext)
{
    m_pConfiguration.load(sConfigurationXml);
    return true;
}

4.2. Giving away the name and version

We do not want to be shy so there is no need to hide the name and version of this wonderful connector:

/**
 * {@inheritDoc }
 */
@Override
public String getName()
{
    return "Extended Filesystem Connector";
}

/**
 * {@inheritDoc }
 */
@Override
public String getVersion()
{
    return "2.3.0"; /* I am lazy, so I simply use the TranslationStudio Version */
}

4.3. Adding operations

The three operations will be implemented in their own classes, so it will be easy here:

/**
 * {@inheritDoc }
 */
@Override
public ITranslationOperation getTranslationOperation()
{
    return new TranslationOperation(m_pConfiguration);
}

/**
 * {@inheritDoc }
 */
@Override
public ITranslationStatusQueryOperation getTranslationStatusQueryOperation()
{
    return new TranslationStatusQueryOperation();
}

/**
 * {@inheritDoc }
 */
@Override
public IQueryOperation getQueryOperation(String sQueryData)
{
    return new QueryOperation();
}

4.4. Reacting to configuration changes

This connector does not need to react to configuration changes. However, if your connector wants provide a web interface which has to connect to a third party system, you may want to use this method to store the configuration in your own configuration file. This file may, in turn, be loaded by the web interface when required. For now, we do not need it.

/**
 * {@inheritDoc }
 */
@Override
public void onUpdateConfiguration(String sConfigurationXml)
{
    /* not needed */
}

4.5. Performing additional tasks.

It is possible to perform additional tasks after translatable XMLs have been processed using the ITranslationOperation. This connector, however, does not need this capability.

/**
 * {@inheritDoc }
 */
@Override
public void onPerformAdditionalTasks()
{
    /* not needed */
}

If you want to check for certain events (i.e. monitoring a folder), this method is the place to implement this particular logic.

4.6. Interacting with the connector’s configuration panel

The method public IQueryOperation getQueryOperation(String sQueryData) allows your connector’s configuration panel to query data at runtime. For example, if you connect to a third party system and want to load all available projects and list them in the configuration panel, this method is what you are looking for.

The configuration panel can use the method IExchangeCommunicatorsendMessage(String sMessage, IQueryRequestAnswerReceived pOnAnswerCallback); to send a message to an instance of this connector.

The setup method will not be called. All data necessary to setup the connector have to be sent in the message itself (i.e. credentials etc.).

This connector does not need such a feature, so we use an empty implementation:

/**
 * Do nothing
 */
private static class QueryOperation implements IQueryOperation
{
    /**
     * {@inheritDoc }
     */
    @Override
    public void query()
    {
    }

    /**
     * {@inheritDoc }
     */
    @NotNull
    @Override
    public String getResult()
    {
        return "";
    }
}

4.7. Processing translatable XMLs

Whenever your connector has to handle new translatable XMLs, an instance of the ITranslationOperation is requested.

First, all candidate XML files to be sent will be added to the instance.

/**
 * {@inheritDoc }
 */
@Override
public void addTranslatableFiles(List<ITranslatableFilegt; vpList)
{
    /**
     * @TODO: store in a list
     */
}

Once all translatable xml files have been added to the operation will be executed:

/**
 * {@inheritDoc }
 */
@Override
public boolean perform()
{
    boolean bSuccess = true;

    /**
     * @TODO: do something with the XML files
     */

    return bSuccess;
}

If you want TranslationStudio to check for finished translations using your connector, you have to provide a list of IDs that will be used to do this.

Depending on your implementation, you may provide an ID for each XML file or one ID per translation "project". It does not matter to TranslationStudio, because your connector needs to handle the status query etc. and it has to be able to obtain all relevant data via these IDs.

@Override
public List<ITransferedFile> getResult()
{
    /**
     * @TODO: provide actual elements
     */

    return Collections.emptyList();
}

If you do not want to check particular IDs but a folder, you may want to implement that logic in the Connector’s onPerformAdditionalTasks() method.

4.8. Monitoring the translation status

TranslationStudio will check the translation status of all transferred files regularly. Your connector has to implement ITranslationStatusQueryOperation to do this.

class TranslationStatusQueryOperation implements ITranslationStatusQueryOperation
{
    /**
     * Get translated data from the TMS
     * @param pFile File Data
     * @param pOutputStream
     * @return success state
     */
    @Nullable
    public boolean downloadTranslation(@NotNull TMSFile pFile, @NotNull IStorableFileStream pOutputStream)
    {
        /*
         * Download the data from the target system. IDs are provided via pFile
         * Simply write the data to the output stream object.
         * The data will only be kept, if this method returns successfully.
         */
    }

    /**
     * Add file status to be queried
     * @param pFileData File Data to lookup in the TMS
     */
    public void addFileToMonitor(@NotNull IFileStatus pFileData)
    {
        /*
         * All files to be monitored will be added via this method.
         */
    }

    /**
     * perform operation
     *
     * @return true, if the result is OK, and FALSE will cause the lookup result to be ignored entirely (e.g. some IOExeption etc.).
     */
    public boolean query()
    {
        /*
         * Check the status of each file provided.
         */
    }
}

You can update the translation status of a file checked in the query method by calling the method IFileStatus.setStatus(…​).

The initial status of each file to monitor is NOT_CHECKED. If the status is set to TRANSLATED, this file will be downloaded. If the status is set to NOT_FOUND, TranslationStudio will forget this file and not retry it in another iteration.

5. Deployment

Deploy your FSM as required by FirstSpirit.

The JAR file of this backend implementation only needs to be deployed to TranslationStudio’s ./lib folder. TranslationStudio will have to be restarted thereafter.