There are a few additional details that apply only to Zen. This chapter:
In order for Zen localization techniques to work in any Zen class, the class must provide a value for the DOMAIN class parameter.
For a simple demonstration of a localized web application, enter the following URI while Caché is running. Substitute your Caché web server port number for 57772:
Caché supports the following process for localizing web application text:
Developers determine where text strings are displayed in the application user interface.
Developers create an XML message file that contains text strings in the original language.
Developers import the XML into a Caché namespace.
This adds new entries to the message dictionary in that namespace.
Developers give the XML to a team of translators.
Translators produce a new XML message file by substituting translated text for the original.
Developers import the new XML into a Caché namespace.
Translated and original texts coexist in the message dictionary.
At runtime, the application chooses which text to display based on the browser default language.
A message dictionary is a simple database of text strings organized by domain name, language name, and message ID:
of each message is a string of up to 32K characters. A message may consist solely of text, or it may also contain one or more parameters specified by %1
name is any arbitrary string. It identifies a group of related text items, such as all messages for a specific application or page.
name is an all-lowercase language tag that conforms to RFC1766
. It consists of one or more parts: a primary language tag (such as en
) optionally followed by a hyphen (-) and a secondary language tag (en-gb
A message ID
is any arbitrary string; it uniquely identifies a message. The message ID only needs to be unique within a domain. You may assign a message ID or allow Caché to assign one, depending on the conventions you use to create the message.
Each user-defined namespace in Caché stores its message dictionary in a subscripted global called ^CacheMsg
. The order of subscripts in ^CacheMsg
is domain, language, and message ID.
For $$$Text macros to work in any Zen class, the class must provide a value for the DOMAIN class parameter.
The easiest way to create a message dictionary is to automatically generate message dictionary entries at compile time, by seeding the CSP class code with $$$Text macro calls. $$$Text automatically creates the message at compile time and generates the code that retrieves the message at runtime, as this topic explains.
The return value of a $$$Text macro is a %String. The correct $$$Text macro to use depends on the format you need for this output string:
$$$TextHTML (applies HTML escaping to the $$$Text result)
returned by $$$Text may be assigned to a variable, which you can use to represent the message in subsequent calls. For example:
Set tmsg = $$$TextJS("Error saving production")
Or, you can simply insert a $$$Text macro anywhere you need a string:
&js<alert('#($$$TextJS("Error saving production"))#: #($ZCVT($ZE,"O","JS"))#');>
$$$Text has the arguments text
, and language
as described in the following table. Only the first argument, text
, is required.
Non-empty string. text
must be a literal string. It cannot use any type of expression syntax. The format used for text
The string actualText
may consist of any of the following items, separately or in combination:
If provided, the textId
is used as the message ID. If @textId@
is not specified, the system generates a new textId
by calculating the 32bit CRC (Cyclic Redundancy Check) of this text. If the textId
is specified and a message already exists with this ID, the existing message is checked to see if it has the same text as actualText
. If not, an error is reported.
||(Optional) A string specifying the domain for the new message. If not specified, domain defaults to the value of the DOMAIN class parameter at compile time and %response.Domain at runtime.
(Optional) An RFC1766
code specifying the language. Caché converts this string to all-lowercase. If not specified, language
defaults as follows:
When you compile a class that contains calls to $$$Text, $$$TextJS, or $$$TextHTML macros, each call generates a message in the message dictionary, with text
, message ID, domain
, and language
as provided by the macro arguments.
If the message text contains arguments (%1
var prompt = '#($$$TextJS("Remove user %1 from this Role?"))#';
prompt = prompt.replace(/%1/g,member.userName);
For the following conventions to work in any Zen class, the class must provide a value for the DOMAIN class parameter.
Zen pages are CSP pages. All CSP localization practices also apply to Zen pages. However, Zen adds the following conventions to make localization even easier:
Any Zen property that has its ZENLOCALIZE parameter set to 1 (true) automatically generates a message dictionary entry. Caché generates the message dictionary using similar conventions as when you use textid=""
in <csp:text> and other localizable CSP tags. That is:
The message text is the string value of the property
The message ID is the 32bit CRC of the text
The domain takes the value of the DOMAIN class parameter
The language takes the value of the browser default language (at runtime)
If a Zen component has any string-valued properties with its datatype parameter ZENLOCALIZE
set to 1, then when Zen generates the %CreatePage
method from the <page> description, Zen automatically localizes the text supplied for those properties.
of the following conditions are true:
You are using components that have properties with ZENLOCALIZE set to 1
You place these components within the <page> element in XData Contents
Your Zen page class has a DOMAIN parameter value defined
Then the code that Zen generates for the %CreatePage
method automatically calls $$$Text to localize each of these properties. Of course, you must provide the translations yourself, as described in Localization Practices.
However, if you do this and also use ZENLOCALIZE and $$$Text as described in this section, all of the mechanisms for delivering these translated strings work automatically. The result is that Zen automatically serves the correct translation to the user by detecting the language locale for the browser or the Caché server at runtime.
The following is a simple example:
If you programmatically build pages (without using <page>) there is no automatic localization of component properties, even if ZENLOCALIZE=1
. In this case your code must issue a call to $$$Text for each one of these strings, as shown in the previous example of generated code for a button caption.
Within your component, to refer to the property simply use the property name with double dot syntax (..title
in this example). You do not need $$$Text within your custom component code unless you have some static text that you wish to localize. Whenever a <page> references your component, your property is automatically localized in the same way as built-in Zen components. For example:
var str = $$$Text('This is a localized string.');
When a method containing this code runs in a Zen page, $$$Text returns a localized string if localization has been enabled by setting the DOMAIN parameter, and a localized version of the string is available. $$$Text takes an optional second argument, which is a DOMAIN name. This argument lets you override the default localization domain:
var str = $$$Text('This is a localized string.', 'MyDomain');
) or double quotes ("
You can use explicit id values for strings, such as "@33@MyString", in the same manner as the server-side $$$Text.
In Caché version 2010.2, the $$$Text function exists but simply returns the string it is passed.
alert($$$FormatText('Ok to delete file %1?', filename));
When a Zen page is rendered, the array of localized strings is created for every type of component and superclasses of that component found on the page. If a component is added to a page dynamically and Zen determines that the class definition for this component must be dynamically loaded, then localized strings are generated for this component as well.
An additional client side equivalent of $$$Text
is the zenText
This function finds a localized text message from a set of client-side text resources, where:
is the id of the text message.
variables are optional. Zen substitutes them for %1, %2, and so on if these variables appear in the message text.
If no localized strings are defined for the supplied id
returns a default string.
You can define a set of localized text messages by overriding the %OnGetJSResources
callback method in your Zen page class, and filling it with Set
statements, such as those that define the resources MyMessage
in the following example:
Method %OnGetJSResources(ByRef pResources As %String) As %Status [ Private ]
Set pResources("MyMessage") = $$$Text("This is my message")
Set pResources("FieldValidation") = $$$Text("Field %1 has an error")
method fills in an array containing pairs of resource identifiers and text strings. %OnGetJSResources
function whenever you need it; for example:
It is possible to define client side resources that apply to every page in the application. To do this, override the %OnGetJSResources
method within the application class rather than in the page class.
As with $$$Text, you must provide the translations for strings such as "This is my message"
yourself, as described in Localization Practices.
However, if you do this and also use zenText
as described in this section, all of the mechanisms for delivering these translated strings work automatically. The result is that Zen automatically serves the correct translation to the user by detecting the language locale for the browser or the Caché server at runtime.
There are some localized text strings already defined as resources for client side localization in all Zen page classes. The following display lists them. The syntax in this example is not complete, because the right-hand side of the display is truncated to keep the lines short. However, each line shows enough to indicate what to expect from this resource if you invoke it using zenText
Set pResources("zenMonthNames") = $$$Text("January,February,March,April,May,June
Set pResources("zenMonthShortNames") = $$$Text("Jan,Feb,Mar,Apr,May,Jun,Jul,Augu
Set pResources("zenDayNames") = $$$Text("Sunday,Monday,Tuesday,Wednesday,Thursda
Set pResources("zenDayShortNames") = $$$Text("S,M,T,W,T,F,S","%ZEN")