Localizing Text in a CSP Application
This chapter describes how to localize text in a CSP application in the common scenario where you are using class-based development. For tag-based development, see “Localization and Tag-Based Development,” later in this book.
Also see the article String Localization and Message Dictionaries.
When you localize the text for an application, you create an inventory of text strings in one language, then establish a convention for substituting translated versions of these messages in another language when the application locale is different.
Caché supports the following process for localizing strings:
Developers include localizable strings within their code. Within a CSP application, the easiest approach is to use class-based development and to use one of the $$$Text macros. In the place of a hardcoded literal string, include an instance of the $$$Text macro (or a related macro), providing values for the macro arguments as follows:
The default string
The domain to which this string belongs (localization is easier to manage when the strings are grouped into domains)
The language code of the default string
For example, instead of this:
&html<<div>"Hello world"</div>>Copy code to clipboard
set hello=$$$Text("Hello world","sampledomain","en-us") &html<<div>#(hello)#</div>>Copy code to clipboard
When the code is compiled, the compiler generates entries in the message dictionary for each unique instance of the $$$Text macro (and its related macros).
The message dictionary is a global and so can be easily viewed (for example) in the Management Portal. Caché provides class methods to help with common tasks.
When development is complete, release engineers export the message dictionary for that domain or for all domains.
The result is one or more XML message files that contain the text strings in the original language.
Release engineers send these files to translators, requesting translated versions.
Release engineers import the translated XML message files into the same namespace from which the original was exported.
Translated and original texts coexist in the message dictionary.
At runtime, the application chooses which text to display based on the browser default language.
For steps 1 and 2, also see the appendix “Localization and Tag-Based Development.”
For information on exporting and importing the message dictionary, see the article String Localization and Message Dictionaries.
Caché provides three related $$$Text macros (in %occMessages.inc, which is included in %occInclude.inc):
$$$Text returns a %StringOpens in a new window
$$$TextHTML returns a %StringOpens in a new window that is escaped for use in HTML
Each of these macros takes three arguments: the default string, the domain to which this string belongs, and the language code of the default string. When code is compiled, the compiler generates entries in the message dictionary for each unique set of values of the arguments.
The %StringOpens in a new window 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") &js<alert('#(tmsg)#: #($ZCVT($ZE,"O","JS"))#');>
Or, you can simply insert a $$$Text macro anywhere you need a string:
&js<alert('#($$$TextJS("Error saving production"))#: #($ZCVT($ZE,"O","JS"))#');>
Formally, the $$$Text, $$$TextJS, and $$$TextHTML macros take the following arguments in order:
Non-empty string. text must be a literal string. It cannot be the value of a CSP runtime expression enclosed in #()# syntax. The format used for text may be:
Where textId is a message ID and actualText is the text of the message.
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 32–bit 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.
|domain||(Optional) 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) RFC1766Opens in a new window code specifying the language. Caché converts this string to all-lowercase. If not specified, language defaults as follows:
Tag-based CSP pages automatically acquire a value for %response.Language from browser settings, so it is available as a default language. This is not true for class-based CSP pages, which must explicitly set a value for %response.Language to use it as a default.
You can assign a value to %response.Language by giving it the return value of the %Library.MessageDictionaryOpens in a new window class method MatchLanguage(), discussed later in this chapter. Given a list of languages and a domain name, this method uses HTTP 1.1 matching rules (RFC2616Opens in a new window) to find the best-match language within the domain.
$$$Text at Compile Time
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.
The first time a message is added to a domain by $$$Text, $$$SessionLanguage is used whether the language argument is specified or not. Subsequent $$$Text macros for the same domain add messages with the same language as the first added message.
$$$Text at Runtime
var prompt = '#($$$TextHTML("Remove user %1 from this Role?"))#'; prompt = prompt.replace(/%1/g,member.userName);
Other Options for Displaying Localized Strings
The easiest way to display a localized string at runtime is to use one of the $$$Text macros as described earlier in this chapter.
This topic explains other ways to retrieve message text from the message dictionary at runtime. If the message text contains arguments (%1, %2, %3, %4) you must also specify the corresponding substitution text before displaying the text on the page.
The %CSP.ResponseOpens in a new window class offers a GetText instance method that enables you to retrieve text from the message dictionary and substitute values for any arguments the message may have. In CSP class code, the currently instantiated %CSP.ResponseOpens in a new window object is represented by the variable %response. This topic refers to the method as %response.GetText.
The method signature is:
method GetText(language As %String = "", domain As %String = "", id As %String, default As %String, args...) returns %String
|domain||(Optional) A string specifying the domain for the message. If not specified, domain defaults to %response.Domain.|
|language||(Optional) An RFC1766Opens in a new window code specifying the language. Caché converts this string to all-lowercase. If not specified, language defaults to the value of %response.Language, which automatically takes its runtime value from the browser settings.|
|id||The message ID.|
|default||The string to use if the message identified by language, domain, and id is not found.|
|arg1, arg2, and so on||Substitution text for the message arguments. All of these are optional, so you can use %response.GetText() even if the message has no arguments.|
The %Library.MessageDictionaryOpens in a new window class offers a FormatText() class method that enables you to substitute text for message arguments. You can use FormatText() when you already have the message text from the message dictionary.
The method signature is:
ClassMethod FormatText(text As %String, args...) As %String
These macros enable you to substitute text for message arguments. You can use them when you already have the message text from the message dictionary:
$$$FormatTextHTML (applies HTML escaping to the $$$FormatText result)
These macros are in %occMessages.inc, which is included in %occInclude.inc.
The $$$FormatText macro returns a %StringOpens in a new window. The syntax is:
The MatchLanguage() Method
You may need to set the Language property of the CSP response. To do so, set the %response.Language property, using the value returned by the MatchLanguage() method of %MessageDictionaryOpens in a new window:
Set language = ##class(%MessageDictionary).MatchLanguage(languages,domain,flag)
This finds the best language match to a language in the list of languages for the specified domain. The method uses HTTP 1.1 matching rules (RFC2616Opens in a new window). The list of languages is a comma-separated list of RFC1766Opens in a new window format language names. Each language in the list may be given an associated quality value which represents an estimate of the user’s preference for the languages specified by the list of languages. The quality value defaults to q=1.
For example, da, en-gb;q=0.8, en;q=0.7 would mean: I prefer Danish, but will accept British English and other types of English. A language from the list matches a supported language tag if it exactly equals the tag, or if it exactly equals a prefix of the tag such that the first tag character following the prefix is a hyphen (-). The special language asterisk (*), if present in the input list, matches every supported language not matched by any other language present in the list.
The language quality factor assigned to a supported language tag is the quality value of the longest language in the list that matches the language-tag. The language that is returned is the supported language that has been assigned the highest quality factor.
The s flag (system) is an optional flag indicating whether system or application messages are to be matched.