Tutorials/Translating The OXID eShop GUI

By Vikram Vaswani

While English is the language you're most likely to see on the Internet, it's important to remember that there is still a large percentage of Internet users for whom English is not their native language. This fact is of particular significance when developing Web applications for a global audience, as these applications cannot achieve worldwide success unless they are capable of internationalization to different languages and customs.

Fortunately, OXID eShop comes with a full-featured i18n implementation that is UTF-8 compliant and written in accordance with OOP principles for easy extensibility. This implementation makes it possible for developers to translate application resource strings to different languages, as well as support local conventions for date and time display. The next few sections will examine this in more detail.

Activating UTF-8 Support
OXID eShop's support for internationalization is centered on two key components:


 * The OXID database: The database stores information about all the languages supported by OXID eShop. It is also capable of holding product titles and descriptions in different languages.


 * OXID translation files: Translation files map static resource identifiers that are used in OXID templates to the corresponding strings in each language.

For proper multi-language support, data in both these components must be encoded with the UTF-8 character set. In particular, to enable UTF-8 support in the OXID database, it is necessary to select the Use UTF-8 character encoding option during the installation process.



Internally, this selection is converted into the following SQL statements. ALTER SCHEMA CHARACTER SET utf8 COLLATE utf8_general_ci; SET character_set_database = utf8; SET character_set_client = latin1; SET collation_connection = utf8; SET character_set_results = utf8; These SQL statements are executed before any of the database tables are created, and they ensure that the OXID database uses the UTF-8 character set by default. However, it's worth noting that UTF-8 encoding uses more bytes than the standard Latin1 encoding, resulting in a larger database size.

Users who fail to select UTF-8 encoding at installation time can still manually migrate their database at a later time to UTF-8 without needing to re-enter any data, by following these step-by-step instructions.

Understanding Key Database Concepts
New languages can be added to OXID eShop through the administration module, at Master Settings -&gt; Languages. Each language is identified by a unique numeric identifier. Languages entered in this way are stored as a serialized string in the oxconfig table with the variable name 'aLanguages'. The identifier for the default language is also stored in this table, with the variable name 'sDefaultLang'.



OXID eShop also allows users to enter language-specific product and category titles and descriptions. In a number of core tables, there exist four fields for the same attribute; each field contains the value of the attribute in a different language. For example, consider the following extract from the oxarticles table, which contains the fields OXTITLE, OXTITLE_1, OXTITLE_2 and OXTITLE_3, each with the product name in a different language.



Similarly, the OXSHORTDESC, OXSHORTDESC_1, OXSHORTDESC_2 and OXSHORTDESC_3 fields contain short descriptions for the same product in multiple languages.

The implication of this database structure is that by default, OXID eShop supports a maximum of four languages. If you need to support more than 4 languages, you will need to manually update the corresponding database tables and add the necessary additional fields using SQL ALTER TABLE commands (as of OXID eShop 4.1.3). Expect this manual schema update to shortly be replaced with an automated process.

OXID eShop supports language-specific unique URLs as well. These URLs are stored in the oxseo database table. As shown in the extract below, this table contains the master list of SEO URLs, together with each one's corresponding internal URL in different languages.



Understanding Translation Files
Look inside any OXID eShop template, and you'll notice that instead of language-specific strings such as "My Account" and "Login", the template uses language-neutral resource identifiers such as ACCOUNT_LOGIN_LOGIN and ACCOUNT_USER_FIRSTNAME. Here's an example: &lt;tr&gt; &lt;td&gt;&lt;label&gt;[{ oxmultilang ident="ACCOUNT_USER_FIRSTNAME" }]&lt;/label&gt;&lt;/td&gt; &lt;td&gt; &lt;input type="text" size="37" maxlength="255" name="invadr[oxuser__oxfname]" value="[{if $invadr.oxuser__oxfname }][{ $invadr.oxuser__oxfname }][{else }][{ $oxcmp_user-&gt;oxuser__oxfname-&gt;value }][{/if }]"&gt; [{if $oView-&gt;isFieldRequired(oxuser__oxfname) }]&lt;span class="req"&gt;*&lt;/span&gt;[{/if }] &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;label&gt;[{ oxmultilang ident="ACCOUNT_USER_LASTNAME" }]&lt;/label&gt;&lt;/td&gt; &lt;td&gt; &lt;input type="text" size="37" maxlength="255" name="invadr[oxuser__oxlname]" value="[{if $invadr.oxuser__oxlname }][{ $invadr.oxuser__oxlname }][{else }][{ $oxcmp_user-&gt;oxuser__oxlname-&gt;value }][{/if }]"&gt; [{if $oView-&gt;isFieldRequired(oxuser__oxlname) }]&lt;span class="req"&gt;*&lt;/span&gt;[{/if }] &lt;/td&gt; &lt;/tr&gt; At render-time, these resource identifiers are automatically replaced with language-specific strings, based on the language currently selected by the user, via the 'oxmultilang' function, an OXID-specific addition to the Smarty template engine. Here's the rendered version of the same template in English and German:





This localization is accomplished through the use of translation files for each supported language, which take care of mapping resource identifiers to the corresponding strings in each language. These translation files are stored at $OXID/out/basic/XX (for the user views) and $OXID/out/admin/XX (for the administrative views), where XX represents the two-character language code from the ISO 639-1 specification. The following screenshot illustrates what this structure looks like in a typical OXID eShop installation.



As illustrated above, OXID eShop uses a number of files for each language.


 * lang.php: Language strings for resource identifiers that belong to the application itself. If you're translating the core templates to a new language, you should add your language strings to this file.


 * help_lang.php: Language strings for resource identifiers used in tooltips and help text. This file is only relevant for the eShop administration module.


 * cust_lang.php: Language strings for resource identifiers that belong to third-party application extensions and modules. If you're translating an existing module, chances are you'll find its resource identifiers in this file. If you're writing a new module, you should add any resource identifiers you create to this file.

It's worth noting that the language strings in the cust_lang.php file override those in the other two files - so if you disagree with the default translations provided by OXID, this file is also a good place to store your revisions, as it won't be overwritten when you upgrade the product.

Where these files contain non-Latin characters, they must be saved using UTF-8 encoding. Some free and commercial text editors with UTF-8 encoding support include gedit on UNIX and Notepad2, EditPad Pro and Notepad (built-in) on Microsoft Windows. For files containing only Latin characters, UTF-8 encoding is not mandatory, but it is preferred to avoid compatibility issues over the long term.

The mapping of resource identifiers to language strings is done via an associative array, as illustrated in the following snippet from the English language translation file, at $OXID/out/basic/en/lang.php file: &lt;?php $sLangName = "English"; $iLangNr   = 1; // --- // RESOURCE IDENTITFIER = STRING // --- $aLang = array( 'charset'                                        =&gt; 'ISO-8859-15', 'fullDateFormat'                                  =&gt; 'Y-m-d H:i:s',

'ACCOUNT_LOGIN_LOGIN'                            =&gt; "Login", 'ACCOUNT_LOGIN_BACKTOSHOP'                       =&gt; "Back to Shop", 'ACCOUNT_MAIN_TITLE'                             =&gt; "My Account", 'ACCOUNT_MAIN_BACKTOSHOP'                        =&gt; "Back to Shop", 'ACCOUNT_NEWSLETTER_TITLE'                       =&gt; "Newsletter", 'ACCOUNT_NEWSLETTER_LOCATION'                    =&gt; "My Account / ", 'ACCOUNT_NEWSLETTER_SETTINGS'                    =&gt; "Newsletter Settings", 'ACCOUNT_NEWSLETTER_SUBSCRIPTIONSUCCESS'         =&gt; "The Newsletter subscription was successful.", 'ACCOUNT_NEWSLETTER_SUBSCRIPTIONREJECT'          =&gt; "The Newsletter subscription has been rejected.", ... ?&gt; Two keys of this associative array are of particular significance:


 * The 'charset' key defines the character set to use when rendering the language strings. The default value for this key is 'ISO-8859-15'. However, if your strings use the UTF-8 character set, it's important to set this to 'UTF-8' to avoid your strings being corrupted at render-time.


 * The 'fullDateFormat' key specifies local conventions for date and time formatting. This should be set to the locale of the target audience, and can make use of any of PHP's standard date formatting specifiers. For example, US audiences would prefer dates formatted as mm/dd/yyyy while European and UK audiences would prefer them formatted using the dd/mm/yyyy convention.

OXID eShop automatically caches the language files in the $OXID/tmp directory to improve performance. It's important to clear this cache directory when adding new language files or modifying existing ones, as new resource identifiers will not be visible otherwise.

Performing Translation
The oxLang object is the main language-related utility class in OXID. On every request, the oxUBase::_processRequest method invokes the oxLang::getBaseLanguage method, which returns the active language by checking a number of sources, including $_GET, $_POST and $_SESSION, for a 'lang' or 'language' variable. If none is found, it uses the default language, as specified in the 'sDefaultLang' variable.

The first time you visit the OXID eShop, the oxStart::appInit method takes care of creating a session variable named $_SESSION['language'] and setting it to the default language, as specified in the sDefaultLang variable.

Here's a flow diagram that explains how this works:



You can try this out for yourself, by adding the $_GET['lang'] argument to the request URL. For example, if language ID #2 corresponds to Spanish, the following URL loads the index page in Spanish: http://oxid.localhost/index.php?cl=start&amp;lang=2 When working with SEO URLs, the oxSeoDecoder::processSeoCall method automatically takes care of retrieving the language ID from the oxseo table and adding it to the request URL as $_GET['lang'].

When rendering a page, the actual interpolation of language-specific strings into templates is accomplished through two custom Smarty plugins: the 'oxmultilang' function and the 'oxmultilangassign' modifier. Both plugins receive a resource identifier as input. Internally, they then acquire an instance of the oxLang object, and execute the oxLang::translateString method with the resource identifier as argument. This method then calls the private oxLang::_getLangTranslationArray method to retrieve the associative array of translation strings for the current language and returns the corresponding string for the resource identifier for interpolation into the template.

Here's a flow diagram that explains how this works:



Adding New Languages
Now that the main components of OXID's i18n implementation, and their interaction, are clear, let's look at a practical example of how this information can be used to translate and localize the OXID user interface for a new language. To make things interesting, I'll use Hindi, the national language of India (my home country).

The first step in creating a localized version of OXID is to create a language translation file for the new language. Accordingly, create the directory $OXID/out/basic/hi and then create the $OXID/out/basic/hi/lang.php language file using an existing file as source. shell&gt; cd /usr/local/apache/htdocs/oxid/out/basic shell&gt; mkdir hi shell&gt; cd hi shell&gt; cp /usr/local/apache/htdocs/oxid/out/basic/en/lang.php lang.php Open this file in your text editor and replace the English-language strings in it with local equivalents. This is probably the most time-consuming part of the entire process; Google Translate is a good tool to get things rolling. Once you're done, update the charset key to 'UTF-8', and save the file using UTF-8 encoding.

Here's a sample of what you might end up with:



The next step is to activate the language in the OXID eShop administration module. Log in to the administration module and navigate to Master Settings -&gt; Languages -&gt; Create New Language. Enter the 2-letter language abbreviation and the full language name, and make sure that the Active field is selected.



Once added, the new language will appear in the language bar at the top left of every OXID page. By default, only the language name will appear. However, you can replace this with an image of the corresponding country flag, by creating a GIF image of the flag with dimensions 18x13 pixels, and saving it to $OXID/out/image/basic/lang/XX.gif, where XX is the two-character country code. In this case, the file will be named $OXID/out/image/basic/lang/hi.gif

Finally, clear the OXID eShop language file cache, typically $OXID/tmp. shell&gt; rm -rf /usr/local/apache/htdocs/oxid/tmp/* And now, when you revisit the OXID eShop index page, you should see the Indian flag in the language bar. Selecting it should re-render the OXID user interface in Hindi, as shown below:



As the above discussion illustrates, OXID eShop has a full-featured i18n implementation that makes it a snap to translate the product interface into multiple different languages. This implementation uses UTF-8 encoding to ensure it works on all modern browsers and also includes support for multi-language product titles and descriptions. So what are you waiting for? Get started with translating OXID eShop to your own language now!