NLS Enabler
The NLS Enabler is a tool for assisting the NLS enablement of an application.
Open an NLS Enabler
Open the NLS Enabler by selecting the Transcript menu item Tools->NLS Tool->Enable Application. A prompter will open with toggle buttons for selecting the types of warnings to be flagged, a list widget to select the application(s) to check, and a toggle button associated with the String Literals warnings to indicate whether or not strings previously marked as ‘ignored’ (comment of $NON-NLS$) should continue to be ignored or should be reanalyzed . Select one or more of the applications and one or more of the types of warnings and hit OK.
+-----------------------------------------+
|Select an Application and Warnings |
+-----------------------------------------+
| |
| ( ) Class References (Step 1) |
| |
| ( ) Concatenation (Step 2) |
| |
| ( ) Stream Selectors (Step 3) |
| |
| ( ) Other Selectors (Step 4) |
| |
| ( ) Character Literals (Step 5) |
| |
| (x) String Literals (Step 6) |
| |
| +-----------------------------------+ |
| |AbtBaseApp | |
| |AbtBaseResourceSavingApp | |
| | ... | |
| | ... | |
| |AbtCodePageUtilitiesApp | |
| +-----------------------------------+ |
| |
| ( ) Skip strings marked as ignored |
| |
| |
| +--------+ +--------+ |
| | OK | | Cancel | |
| +--------+ +--------+ |
| |
+-----------------------------------------+
Warnings detected include:
1. Class References
Identify direct references to potentially problematic Classes. These classes are in the class variable WarningClasses of the NlsClassWarning class. The default warning classes are String and DBString.
Default is disabled.
2. Concatenation
Identify direct references to potentially problematic concatenation selectors. These selectors are in the class instance variable warningSelectors of the NLSConcatenationWarning class. The default warning selector is #,.
Default is disabled.
3. Stream Selectors
Identify direct references to potentially problematic stream-oriented selectors. These selectors are in the class instance variable warningSelectors of the NlsStreamSelectorWarning class. The default warning selectors are #nextPut:, #nextPutAll:, and #next:put:.
Default is disabled.
4. Other Selectors
Identify direct references to potentially problematic general selectors. These selectors are in the class instance variable warningSelectors of the NlsGeneralSelectorWarning class. The default warning selectors are #asCharacter, #asString, #asDBString, and #printString.
Default is disabled.
5. Character Literals
Identify direct references to character literals.
Default is disabled.
6. String Literals
Identify direct references to String literals. String warnings may be replaced by message references using the NLS Enablement Browser.
Default is enabled.
NLS Enablement Browser
The NLS Enablement Browser has the following layout:
+-----------------------------------------------------------------------+
|NLS - <app> [<no>pool] 1 |
+-----------------------------------------------------------------------+
|File Edit Methods Info Generate Checking Options 2 |
+-----------------------------------------------------------------------+
| 3 |
| |
| |
| |
| |
| |
+---------------------------------------|--------|------|---------------+
| 4 | -/- 5 | Next | Previous |
+---------------------------------------|--------|------|---------------+
| 6 |
| |
| |
| |
| |
+-----------------------------------------------------------------------+
| 7 |
+-----------------------------------------------------------------------+
The NLS Enablement Browser is used for viewing warnings and NLS enabling methods. Only methods flagged by the selected warnings are displayed. The browser has seven main areas:
1. The title of the browser shows the application currently being edited and the pool the messages are going into.
2. The menubar contains File, Edit, Method and Info menus that have all of the functions provided by ENVY for other browsers. In addition, the Edit menu has entries for replacing string literals with messages. The Generate menu supports creating the Pragma to hold the NLS messages. The Checking menu is used for regenerating the list of methods, and for managing the associated Pool Dictionary information. The Options menu has a set of options for browser behavior when enabling methods. These menus are discussed in more detail later.
3. The list of the methods with warnings found by the NLS Enabler.
4. The description label shows the description of the currently selected warning if there is one.
5. The count label displays the number of warnings and which one is currently selected (e.g. 3/5). Beside it are the Next and Previous buttons which are used to increment or decrement the count.
6. The text widget that displays the text of the currently selected method.
7. The label that displays the info for the currently selected method.
The browser is similar to the ENVY Methods Browser. It has a list widget for displaying the methods and a text widget for displaying the text of the selected method. When a warning is displayed, the description of the warning is displayed in the description label. The Next and Previous buttons are used for moving back and forward through a list of warnings.
The warnings for a method can be browsed by selecting a method and stepping through the warnings. The number of warnings is displayed in a label beside the Next and Previous buttons.
Externalizing Strings for an Application
To externalize the strings in an application the NLS Enabler is opened with String Literal warnings enabled. Strings flagged by String Literal warnings can be replaced by named or indexed messages. A named message is a message that stays resident in the image whereas an indexed message is loaded on demand. A message should be kept resident if it is used frequently, otherwise it should be load on demand to keep the size of the image to a minimum.
Typical Externalization Process
The following steps are the typical stages in the externalization process.
1. Replace strings built using the #, selector. Replace with bindWith:. This will require someone to check code and replace the references manually.
Example:
'The time is', Time now printString, '.'.
should be replaced by
'The time is %1.' bindWith: Time now printString.
2. Externalize the strings using the NLS Enabler.
3. Open the NLS Enabler again, select the class warning check and check the generated warnings for the application.
4. Open the NLS Enabler again, select all checks and check the generated warnings for the application.
It is recommended that for testing purposes the strings for other locales be generated by prefixing the text of all messages with a locale specific string (for instance, a u with an umlaut for testing a German locale). This allows for testing of other locales before the translators are done creating translations for them.
Setting the Pool and Catalog
Externalized strings are referenced using an EsPoolDictionary (a pool) and an NlsMessageCatalog (a catalog, not referenced in the method source). The catalog maintains an external file that has the resident and indexed messages for a catalog. A pool dictionary holds onto the catalog and its resident messages in the image and can be used to access the indexed messages in the catalog. A method refers to the pool to get the indexed and resident messages.
When enabling an application the pool and catalog to be used must be specified. If the user attempts to replace a string with a pool reference without setting the pool first, the Pool Definition prompter is opened. The Pool Definition prompter can also be opened using the Checking->Change Default Pool ... menu option.
The Pool Definition prompter has 5 fields to be entered:
1. Pool Name:
The text widget that shows the name of the pool dictionary that will maintain the message strings. This value must be a non-empty single-byte String. The first letter must be capitalized. The select button beside the pool allows the user to select a pool.
2. Catalog Path:
A combo box that gives the user the choice of the working directory or one of the directories in Locale nlsPath.
3. Catalog Name:
The text widget that shows the name of the catalog. The select button beside it allows the user to select a catalog that exists in the selected catalog path.
4. Prefix:
The text widget that shows the prefix to be used for named messages. Prefixes are only needed if the user wishes to use default names keys (generated using the prefix and a counter e.g. MxCG1). If not this field can be left blank.
5. Locale:
The locale that the messages will be saved as. This must be in the form language-territory-characterSet. Generally one locale is kept up to date by developers so it is best to choose the same locale as the other developers of the application.
When the OK button is selected the settings are saved. If the pool selected is a new pool the user will be asked if they wish to generate code. The code generated will be shown on the Transcript and will be installed in the application if the user chooses to generate code. The code generated appears in four places:
1. #loaded:
A method in the class of the application that calls the localize method. If the method already exists the required loaded code is added to the existing code if it is not currently in the method.
Example:
loaded
"The receiver application has just been loaded.
Perform any initialization."
self localize. "#localize must reset any cached strings"
2. #localize:
A method in the class of the application to localize the messages in the pools when the locale changes. If the method already exists the required localize code is added to the existing code if it is not currently in the method.
Example:
localize
"Localize the receiver application to the default
messages locale. This method MUST reset any strings that
are being cached by the application or its classes. This
must be done after the relocalizeTo: line."
NlsCatKRN relocalizeTo: self defaultMessagesLocale.
3. toBeLoadedCode:
Code that is executed before an application is loaded. This is to declare the keys of a pool before any applications dependent on it are loaded. If the toBeLoadedCode already exists the required toBeLoadedCode is added to the existing code if the required code is not currently in it.
Example:
'"$NLS-START$"
(Smalltalk declarePoolDictionary: #NlsCatKRN) value
declareFromCatalog: ''krn''
locale: #(''english'' ''us'' ''ansi-ascii'').
"$NLS-END$"'
4. wasRemovedCode:
Code that is executed after an application is removed. The #wasRemovedCode: removes the catalogs created from the Smalltalk dictionary when executed. If the #wasRemovedCode: already exists, the required #wasRemovedCode: is added to the existing code if the required code is not currently in it.
Example:
'"$NLS-START$"
Smalltalk removeKey: #NlsCatKRN asGlobalKey ifAbsent: [].
"$NLS-END$"'
If the catalog does not have its compatabilities set, the user will be asked if they wish to set default compatabilities. Compatabilities are arrays used to define compatible locales. The default is #((english us ansi-ascii) (* * *)) which indicates that all locales are compatible with the english us ansi-ascii locale. The compatabilities for a catalog can be set using the Catalog File Editor. See the NLS Programmers Reference for additional information on compatabilities.
Replacing Strings with Message References
A literal string may be replaced by a message reference or marked as ignored. Replacement can occur if a string is selected, if the pools and catalogs to be used are defined and if the string is not part of a literal array.
If a string is not selected the menu options will be grayed out. If the pool or catalog to be used is not set the pool selection browser will be opened first. If the pool and catalog values are entered the replacement can proceed. If a string is in a literal array the replacement will not be allowed. This is to prevent the accidental bugs that can occur by replacement. For example the array #('aa' 'bb' 'cc') would be replaced by something of the form #(Mx1 Mx2 Mx3) which would return #Mx1 #Mx2 #Mx3 rather than 'aa' 'bb' 'cc'.
There are three entries on the text and edit menus for this:
1. Replace With Name:
Replace by a named message (load resident). If the prefix is set the name suggested will be the next resident message name based on the prefix and a counter stored in the catalog file or an existing key if the same string had been previously externalized using the selected catalog. Otherwise an empty prompter will be shown. The entered value (if it is not empty) will replace the literal string in the text of the method and the literal string will be used as a comment that will be appended at the end of the line. For instance the literal string 'Literal String' would have "$NLS$ Literal String $NLS$" appended at the end of the line when replaced by a message reference.
2. Replace With Index:
Replace by an indexed message (load on demand). The user will be prompted with the next available index in the catalog or the index of the string if it already in the catalog, but this value can be changed by the user. The entered value (if it is not empty) will be used to build a message reference (e.g. NlsCatKRN indexedMsg: 7) to replace the literal string in the text of the method and the literal string will be used to make a comment that will be appended at the end of the line in the same manner as Replace With Name.
3. Mark As Ignored:
Append a comment at the end of the line indicating that the string is to be ignored by the tool if the methods are generated again, either by opening another NLS Enabler on the application or by regenerating the methods using the Method ->Update menu option. If all of the literal strings left in a method are marked as ignored the method will not be flagged next time the warnings are generated.
Options and Methods Menu
The methods menu is an extension of the default methods menu of the methods browser. It has one more entry, Update. When Update is invoked the methods displayed in the browser are recalculated as if the browser was just opened.
The Options menu has a set of options for enabling the applications. They are:
1. Add Message Comment:
When enabled, when a string is replaced by a message a comment is created using the value of the message. The comment is appended to the end of the line. When disabled the string is replaced without generating the comment.
Default is enabled.
2. Prompt For Message:
When enabled the user is prompted before a replacement is made for a string. Otherwise the replacement is done automatically. The replacement is not done if no named message can be generated, i.e. if the prefix is empty.
Default is enabled.
3. Warn For Indirect:
If a method is in an extension of a class in an application, references to named messages must be indirect (i.e. NlsCatCFS residentMsg: 'Joe') since the pool cannot be added to the class definition in an application where a class is extended. If Warn For Indirect is enabled then the user is warned before an indirect replacement begins, otherwise it is done without warning.
Default is enabled.
Advanced Warning Topics
The default warning types are Class, Selector, String and Character warnings. To add a warning, a subclass of NlsWarning must be created. An NlsWarning has a position (a Point - start and finish position in the text) and a warning (an Object). They must be set when the warning is created. They are used by the browser to highlight the warning and to show the description of the warning. Any scheme may be used to determine the existence and positions of warnings. The existing warnings are found using the parse tree of the method. The API required to use them is described later.
NlsWarning Overrides
The methods in NlsWarning that may be overridden by warning classes are:
1. #findWarningsForMethod:
findWarningsForMethod: aCompiledMethod checkInDictionary: enabledWarningDictionary
"Return any warnings associated with aCompiledMethod. Check
only those NlsWarning classes marked as enabled in
enabledWarningDictionary.
ARGUMENTS
aCompiledMethod : CompiledMethod.
enabledWarningDictionary: Dictionary key: NlsWarning subclass value: Boolean
RESULT
Collection of NlsWarning"
2. #hasOptimizedHasWarningsSearch
hasOptimizedHasWarningsSearch
"Return whether or not the class has an optimized method of
finding out whether or not a method has a warning. This method must
be more efficient than the default method that uses the parse tree.
ARGUMENTS
None.
RESULT
Boolean"
3. #hasWarningsForMethod:checkInDictionary:
hasWarningsForMethod: aCompiledMethod checkInDictionary: enabledWarningDictionary
"Return true if there is at least one warning associated with
aCompiledMethod. Check only those warning classes marked as enabled
in enabledWarningDictionary.
ARGUMENTS
aCompiledMethod : CompiledMethod.
enabledWarningDictionary: Dictionary key: Class value: Boolean
RESULT
Boolean"
4. #hasWarningsForMethodOptimized:checkInDictionary:
hasWarningsForMethodOptimized: aMethod checkInDictionary: enabledWarningDictionary
"Return true if there is at least one warning associated with
aCompiledMethod. Check only those warning classes marked as enabled
in enabledWarningDictionary. Do the check in a manner optimized for
the particular warning.
ARGUMENTS
aCompiledMethod : CompiledMethod.
enabledWarningDictionary:
Dictionary key: Class value: Boolean
RESULT
Boolean"
5. #isConcreteWarningClass
isConcreteWarningClass
"Return true if this is a concrete warning class, false otherwise.
ARGUMENTS
None.
RESULT
Boolean"
6. # label
label
"Return the label for the warning.
ARGUMENTS
None.
RESULT
String"
7. #description
description
"Return a description for the warning.
ARGUMENTS
None.
RESULT
String"
8. #NonNlsCount:
nonNlsCount: aCompiledMethod
"Return the number of hits to be ignored in aCompiledMethod.
ARGUMENTS
aCompiledMethod: CompiledMethod.
RESULT
Integer"
EsParseNode Overrides
Rather than overriding some or all of the above mentioned methods the check can be done by using a parse tree node (EsParseNode subclass). If this is done, none of the NlsWarning methods need be overwritten but some or all the following methods in EsParseNode will have to be overridden by the EsParseNode to be used for the check.
The methods are:
1. #extractNLSWarnings:
extractNLSWarnings: enabledWarningDictionary
"Extract any NlsWarnings associated with this node using
checks enabled in enabledWarningDictionary. If there is not one
return nil.
ARGUMENTS
enabledWarningDictionary:
Dictionary key: String value: Boolean.
RESULT
Collection of NlsWarning or nil"
2. #nlsShouldProceed
nlsShouldProceed
"Return true if the subnodes should be checked and false
otherwise.
ARGUMENTS
None.
RESULT
Boolean - true by default"
3. #nlsWarningClass
nlsWarningClass
"Return the NlsWarning class that represents the class that
could have a warning generated using this node.
ARGUMENTS
None.
RESULT
NlsWarning class or nil"
Non - Browser Settings
The following methods can be used to set other options for the enablement browser:
1. NlsWarning class>>enabled:
NlsWarning class>>enabled: aBoolean
"Set whether or not the check associated with this class is
enabled. By default the answer is false for abstract classes and
true for concrete classes.
ARGUMENTS
aBoolean: Boolean
RESULT
unspecified"
2. NlsClassWarning class>>warningClasses:
NlsClassWarning class>>warningClasses: aCollection
"Set the collection of classes that will cause a warning.
ARGUMENTS
aCollection: Collection of Class.
RESULT
unspecified"
3. NlsSelectorWarning class>>warningSelectors:
NlsSelectorWarning class>>warningSelectors: aCollection
"Set the collection of selectors that will cause a warning.
ARGUMENTS
aCollection: Collection of Symbol.
RESULT
unspecified"
4. NlsStringLiteralWarning class>>ignoreMarked
NlsStringLiteralWarning class>>ignoreMarked
"Return whether or not Strings marked as ignored should not
be flagged for a warning. Default is true.
ARGUMENTS
None.
RESULT
Boolean"