The two-character language code according to ISO 3166-1 is the basis for
controlling the language in portals. Multilingual does not just mean presenting
texts in a language-dependent manner, but also presenting number and date
values in the respective country. Even colors can be interpreted differently
depending on the culture. Graphics with lettering also get a language-dependent
aspect. Even the question whether the user reads from left to right or vice
versa can play a role. There are therefore various details that need to be
considered and implemented when creating a multilingual portal - regardless
of which technology is used. You will find what you need to know for this here.
2. Portal properties - Regional settings
In the portal properties, accessible via the
Portal menu / Portal properties,
you will find the
Regional settings. The portal can be configured for international use here.
Information about each of the options can be found here:
The additional control
Language switch from the
Design module is
used for switching the language in the browser. The languages, which should be
selectable here, must be activated in the
Regional settings.
To switch to another format, used to format, for example, the date according
to the region, the additional control
Locale switch from the
Design module is used.
The formats, which should be
selectable here, must be activated in the
Regional settings.
4. Find and translate element titles
In the modules Applications,
Design and
Processes
you can search for elements
that are missing titles in one of the portal languages.
The full list of element titles from applications and processes
can be exported as an XML file, translated and then reimported
via the respective main menu.
5. Language switch in modules
You can switch the language that the element titles in each of the
modulesPortal Manager are displayed in,
with the globe symbol in the
symbol bar.
6. Portal menu
For applications, which are integrated into the
menu structure,
you can define that the application title should be used as the menu item.
If the title
is already defined in multiple languages, the values for each of the portal languages
will be used.
If the
Use title of application setting is disabled, you need to define the title for the
menu item in each of the portal languages.
You can also define multilingual titles for all other objects such as menu folders, links
or separators, these will then be displayed in the
language selected
by the user.
7. Layout
Because each portal layout
is an assembly of various containers that,
especially for barrier-free concepts, should be understood by screen readers, the
containers also need to be provided with
multilingual titles.
Custom
language constants cannot be used in the layout. Each of the
elements in the layout uses
correspondingly defined portal constants.
8. User settings
In the Users module,
regional-specific settings can be defined in the
properties of users.
The settings defined here for time zone, language, format and layout
overrule the settings made in the
Regional settings of the portal.
9. Applications
9.1 Language constants in applications
In the Applications module,
using language constants is not only a good idea for multilingual applications
but all applications can benefit from the use of the constants.
Because the same text appears in multiple locations, these can be modified
much more easily via a central constant. Therefore, United Planet recommends
developing applications from the very beginning with application constants.
Application constants
can be added, edited and deleted via the
Application menu / Manage language constants.
9.2. Multilingual element titles
You can find out how to provide elements with multilingual titles
here.
you can select
User-defined values
as the source for the list in the element.
Constants can also be used here.
The displayed value from the constant and the saved value must be identical in this case.
9.4. Multilingual data records
You can discover how to enter data records in different languages and then
display them correctly based on the respective portal language setting
in the Advanced Techniques workshop
Multilingual data records.
9.5 Multilingual data with doubled primary key
In order to make data available in multiple languages, these can be saved in a
data group using the principle of a doubled primary key.
This is made up of a key and an ISO language code. For control tables, e.g. for statuses,
the key should be a descriptive string. For all other data, it should be a
GUID.
Before saving the application, you can changed the data type of the existing
primary key to string on the
Expert tab, meaning it will be
saved as a GUID later. For the second key, a new data field called
"Language" is created, again before the application
is published.
When data records are entered, both primary keys need to be populated with
values so that errors do not occur when the data is processed.
To do that, the language data field is connected to a
edit field in the
Hidden area.
The field is provided with a "customdefault"
expert attribute with the value
"$lang" and is preallocated with the
current portal language.
If the primary key should also be generated automatically using a GUID,
the value "$Unique.newGuid()" can be entered
for the "customdefault"; this field can also
be moved to the Hidden area.
A common scenario for this is using multilingual master data, such as
statuses, categories, in moving data records via a
reference.
If a reference to a data group with two primary keys is created,
the session variable "Language" must be assigned
for the primary key. The other primary key will be left with the setting
"Will be created automatically".
Using the reference, Intrexx will now always provide the corresponding language text in the browser.
8.6 Multilingual data with a core data record as a reference
This option makes sense, if a large amount of data, which is language-independent,
is defined alongside multilingual texts. This means that the language-independent data
is recorded in the core data record and the text information is stored in the
data record.
The text data group is referenced in the Tree data group and the
language is set to Current portal language. Here, it is important
that the entries saved in the labelling table receive the ID of the
tree entry as the primary key in order to create a synchronicity
between both of the data groups and the data records.
In the Tree control's configuration, the label text will be assigned
as the title using the reference. This will be identified and
then displayed, based on the defined portal language.
When creating a new tree entry, a special procedure must be implemented
in this constellation because a label is not saved in the tree's data group.
For this reason, an edit page with a text edit field without data link is created.
When Save is clicked, a JavaScript with
the entered text will be transferred to the process as a request parameter.
function newTreeEntry(oAction)
{
var oDescription = getElement("GUID_EDITFIELD");
oAction.oUp.oTarget.addParam = Helper.setQsValueByParam(
"rq_customDescription", oDescription.value, oAction.oUp.oTarget.addParam);
return true;
}
Using a Data group event handler, which monitors whether a new tree entry has
been made, the request value is read in the executed Groovy script, the
corresponding entry is added to the labelling table and at the same time,
the data records for the language variables are created. Furthermore, the
reference value is written to the data record in the labelling table in
the tree's data group.
def conn = g_dbConnections.systemConnection
def l_strActualLang = g_language
def l_strDescription = g_request.get("rq_customDescription")
def l_intTreeId = g_record["GUID_ID_TREEDATASET"].value
def l_aLanguages = g_portal.defaultLocale.languages
// Generate data records
l_aLanguages.each { language ->
def l_strIsoLang = language
g_dbQuery.executeUpdate(conn, "INSERT INTO
DATAGROUP('GUID_DATAGROUP_LABELLINGTABLE')
(TREEID, LANG, STRNAME) VALUES (?,?,?)") {
setInt(1, l_intTreeId)
setString(2, l_strIsoLang)
if(l_strIsoLang == l_strActualLang)
{
setString(3, l_strDescription)
}
else
{
setString(3, "Translate: " + l_strDescription)
}
}
}
g_dbQuery.executeUpdate(conn, "UPDATE
DATAGROUP('GUID_DATAGROUP_TREE') SET LID = ?,
REF_NAME = ? WHERE LID = ?") {
setInt(1, l_intTreeId)
setInt(2, l_intTreeId)
setInt(3, l_intTreeId)
}
10. Create language variables via a process
Usually, a multilingual data record is always created based on one language.
If the creator has set their portal to English, the data record is created
in English. Additional language variables must then be generated. This can be
done manually but represents a lot of work for multilingual portals.
A process can take over the preliminary work and automatically generate
the data records for the respective language variables for each language
in the portal. This makes it easier to add additional portal languages
at a later point in time. Even in this case, a process can create
the data records for the new language variables retroactively. This is
especially important for master data, such as status definitions,
to ensure that the application is fully operational in the new language.
The following Groovy script demonstrates, by way of example,
how to create data records for missing language variables in a
data group when a new portal language is added.
def conn = g_dbConnections.systemConnection
// Identifies the currently available portal languages
def l_aLanguages = g_portal.defaultLocale.languages
// For each portal language
l_aLanguages.each
{
// Check whether at least one entry for this language is found in
// an application table
def l_strIsoLanguage = it
def l_intLanguageDetect = g_dbQuery.executeAndGetScalarIntValue(conn,
"SELECT COUNT(*) FROM DATAGROUP('GUID_DATAGROUP') WHERE LANG = ?", 0) {
setString(1, l_strIsoLanguage)
}
if(l_intLanguageDetect == 0)
{
// Language not found in data, add language Variants
def stmtData = g_dbQuery.prepare(conn,
"SELECT STRID FROM
DATAGROUP('GUID_DATAGROUP')")
def rsData = stmtData.executeQuery()
while (rsData.next())
{
def l_strTypeId = rsData.getStringValue(1)
g_dbQuery.executeUpdate(conn, "INSERT INTO
DATAGROUP('GUID_DATAGROUP')
(TREEID, LANG, STRNAME) VALUES (?,?,?)")
{
setString(1, l_strTypeId)
setString(2, l_strIsoLanguage)
setString(3, "Translate")
}
}
rsData.close()
stmtData.close()
}
}
11. Multilingual search
11.1 Language-isolated-search
The language-isolated-search takes the current portal language and only returns
content that corresponds to this language in the search results. To do that, the
portal language needs to be filtered in the search configuration. The
"Language" data field is compared to the system value
"Language". The respective search results
will only be shown if these match.
11.2 Search with user-selected language
You can enable the user to select the language by using
facets.
To begin with, the facet "Language" should be defined.
In the search configuration, the facet is connected to the
"Language" data field.
The configuration now provides every language, which was found during the search,
as an option for filtering the search results. Because the search engine cannot
interpret the facets, the ISO codes are shown as the options.
12. Multilingual emails
12.1 Email to one recipient
If a user sends an email, their language setting can be identified in a process.
The process can use the identified language in an email sent with an
Email action.
The subject can be defined by translating the language-dependent subject.
To make the language control more individual, the language can be identified
and then written to the processing context
with a Groovy script before the Email action is performed.
// Language of current user
def l_strLanguage = Locale.forLanguageTag(g_language).getDisplayLanguage(new Locale(g_language))
// Language of any user
// (via their GUID and user object)
def l_objUser = g_om.getUser(l_strUserGuid)
def l_strLanguage = objUser.getDefaultLanguage()
// Write language to processing context
g_sharedState.maillanguage = strLanguage
In the properties dialog
of the email action, the parameter "language"
can be added on the Expert
tab. The identified language can be transferred as the value with
"urn.sharedState.maillanguage".
The subject can also be defined using a
language constant.
With the corresponding Groovy method, the language constant's content is read
based on the language identified in the previous step.
// Identify subject from language constant and write
// to processing context
g_sharedState.subject = g_i18n.application("APP_GUID").language(l_strLanguage)["MYMAILSUBJECT"]
The value from the processing context is then entered in the static,
language-independent subject of the Email action with
"urn.sharedState.subject".
If the email is sent via CC and BCC to one recipient with different languages
in each case, this method cannot be used. This would require separate handling
with an individual email action in each case in the process chain. The message
text could also be identified in a preceding Groovy action and then written
to the processing context. The processing context can then be read in Velocity
and from that, the email body can be generated.
12.2 Email to distribution list
There are two approaches to sending emails to a distribution list if
these should be sent multilingually:
An email action is created for each language where the recipients
are selected based on their defined language.
The email is generated with Groovy script while taking the language
of each user into account. However, the message body needs to be constructed
manually in this case.
A possible construction is shown below. In this case, however, only user objects
can be recipients via the distribution list.
import de.uplanet.lucy.util.TextUtil
import de.uplanet.lucy.server.mail.GroovyMailBuilder
import de.uplanet.lucy.server.mail.MailUtil
import de.uplanet.lucy.server.portalserver.PortalServerPath
import de.uplanet.lucy.server.composer.UrlBuilder
// Content of a distribution control (Recipient GUIDs)
def l_strRecipients = g_record["640...DAC"].value
def l_aReceipients = TextUtil.stringToList(strRecipients)
aReceipients.each {
def strEmail = g_dbQuery.executeAndGetScalarStringValue(conn, "SELECT
STRMAILBIZ FROM VBLUSER WHERE STRGUID = ?", null) {
setString(1, it)
}
if(strEmail != null && strEmail != "")
{
def mail = new GroovyMailBuilder().composeMail {
headers = [
"X-IX-Share": "intrexx-share-notification",
]
from = MailUtil.getDefaultSenderAddress()
to = strEmail
subject = l_mailHelper.getMailTitle()
contentType = "text/html; charset=UTF-8"
body << """<html> … </html>"""
}
mail.drop()
}
}
13. Language switches
To a large extent, you can avoid using language switches in programming codes
with language constants.
However, the following constructs could be useful in special cases.
To create a language switch in JavaScript, the selected portal language
needs to be identified with the "oHtmlRoot"
object.
var l_lang = oHtmlRoot.oUp.oFormatInfo.lang;
switch(l_lang)
{
case "de":
// Code for German
break;
case "en":
// Code for English
break;
:
default:
// Fallback, if the language is missing (default portal language)
}
if(l_lang == "de")
{
// Code for German
}
else if(l_lang == "en")
{
// Code for English
}
:
else
{
// Fallback, if the language is missing (default portal language)
}
The "oHtmlRoot" object can also be used to identify
the defined default language.
var l_defaultLang = oHtmlRoot.oUp.oFormatInfo.defaultLang;
To construct language-dependent switches in the Velocity context,
the Intrexx system variable $lang can be used that contains the current
portal language.
#if($lang == "de")
// Language-dependent code
#elseif($lang == "en")
// Language-dependent code
#else
// Fallback for missing language
#end
Different options are available for generating multilingual data in a
Groovy script or to control certain actions based on the language.
// Current portal language
g_language
// Default portal language
g_defaultLanguage
// Current portal language via the request parameter rq_Lang
g_request.get("rq_Lang")
// User's default language from the current session
g_session.user.getDefaultLanguage()
A language switch can be created for various purposes using a switch case construction.
switch(g_language)
{
case "de":
return german
break
case "en":
return english
break
default:
return english
}
14. Multilingual graphics
If graphics are managed via data groups, these can be defined for each
language-dependent data record. It is much more difficult to display
language-dependent graphics in the layout, as this is not provided for
technically. One option for this is to provide the graphic with the ISO code
in the filename and then to construct the URL with Velocity using the
current portal language. The following ActionControl generated a DIV container
with a link to the portal homepage as well as the language-dependent
embedding of a graphic. It is important to name the graphic with
"logo_<iso-languagecode>.png".
The control assembles the filename dynamically by using the current
portal language "$lang".
In every language constant with the type
Constant is evaluated as a Velocity expression,
Velocity variables can be embedded when these are used on application pages.
For this to work correctly, the variable needs to be available in the page
context. In the following example, the variable "$Rooms"
is written into the text to add a dynamic statement - the number of rooms - to the
static text.
Constant text:
There are $Rooms room(s) for this building!
Output: There are 10 room(s) for this building!
16. Language switch with flags
A language switch with flags only makes sense for a few portal languages, and
only in combination with the language name. For languages such as English,
that are represented by multiple countries and therefore flags, a
language switch with flags is not always ideal. Intrexx already delivers
a set of graphics with flags that possess the ISO code as their name.
To begin with, add a new file called
"languageswitch_flag.vm" to the
portal directory
\internal\system\vm\html\actioncontrol
and copy the Velocity code below into this file.
After restarting the Portal Manager, the additional control can be selected
accordingly and added to the layout.
17. XML export format for language constants
The XML format for
importing and exporting language constants collects every text constant together
in the block <texts>.
Every constant is defined via an <element>.
The constant name is stated as the key, whereas the value of the
"type" always needs to be "constant".
Every constant is defined via an <element>.
Each language variable is written as a <text>
element under the <element>.
As well as the ISO code, the availability
("ALL", "VELOCITY", "JAVASCRIPT") of the constant
is also defined in the "language" parameter.
Language texts defined directly in the controls, are identified
in the <element> tag with the parameters
"type" (object type) and "key"
(control GUID).
In the <text> tag, further information
about the export is stated in the paremeters
"timestamp" (export time stamp), (application or portal),
"guid" (application GUID) and "refLanguage" (reference language).
Parameter
Value
Description
timestamp
YYYYMMTTHHMMSS
Export time stamp
type
application portal
States whether the export comes from an application or a portal