Writing Translatable Dexterity Code
When Great Plains Software worked on translating the Dynamics product into multiple languages they came across a number of problems. Even though Dexterity was designed to allow easy translation, the international team found that developers needed to follow certain "Best Practices" to truly have a translatable application.
Tom Irsfeld, who headed up the international team for a number of years, worked with Dave Gaboury on the Dexterity development team and they created the following rules. You might have seen this rules before, but I will be explaining why they are important as well.
To write Dexterity code that can be easily translated, use the following guidelines:
- Do not use hard-coded or literal strings. Use messages instead.
Literal strings cannot be changed without editing the source code.
- Do not concatenate messages. For example, use the %1, %2, etc. placeholders and substitution.
Joining or concatenating strings assumes that the grammar and the order of sentence construction for different languages is the same as the language you have written the original message in.
- Do not use a message resource if it should not be translated. Use a constant instead.
As the translation is possibly going to be performed by someone other than the developer, they will not know when a message should not be changed. Anything which should not be translated should not be using a message resource.
- Do not assume anything about the size of a message resource. Overestimate the possible length of a message resource.
When returning messages to windows or reports, make sure that you leave additional space and characters in fields to avoid string overflow errors or words being truncated. The translated words might be much longer than the original words. For example: Using a non-editable local dummy string to have a message on the screen that can change, make the dummy string's keyable length double what you actually need.
- Do not use a single message to do the work of many messages. Create separate messages for each use.
For example: "This %1 cannot be found, please use %2 maintenance to enter this %3.". Then you can substitute Customer, Vendor or Item into the placeholders. While this might sound like a more efficient use of resources, it has two problems. 1) the person doing the translating will not understand how this message is to be used; 2) in some languages the sentence structure needs to be different depending on the nouns being used (eg. French nouns have a gender).
- Do not use strings that end in spaces or messages that end in spaces. Trailing spaces cannot be seen, and trailing spaces are lost.
When a translator is changing the strings and messages, they will not see spaces and so the new translated strings or messages will be missing the strings. As we will not be concatenating any messages (see Rule 2) there is never a need to have a space at the end of a message.
- Do not use messages to assign key values in tables. Use constants.
If a message is used for a key value in a table, it will be translated and so cause the data in the key field to be different depending on language. Using a constant will prevent the translation from occurring. You might know of the PMTRN vs PMTRX issue with International English installs and the pain this caused.
- Do not use text in bitmaps. Resources can have both text and pictures assigned to the resources.
If text is used in bitmaps (Pictures or Native Pictures), the bitmap itself would need to be edited as part of the translation. This was an issue in old versions of Dexterity as it was not possible to have an image and text on a button. As you can now have text and a bitmap on a button, there is never a requirement to have text directly in the bitmap.
- Maximize the size of the fields for prompts. Leave space for prompts to be longer after they are translated.
When laying out a new window, make sure that the prompts have at least a third again of space before the actual field starts. This will allow space for translated words that could be longer (see Rule 4).
- When you substitute data into messages, only substitute data from tables or from calculations.
When substituting into placeholders on a message, do not use a string from another message. Only substitute data or calculations. The reasoning is described in Rule 5.
- Do not assume that all letters are between a and z or between A and Z. Also consider extended characters.
Many other languages have characters that go beyond the standard 26 letters in the English alphabet. If you have code which checks for letters, don't check for the range of ASCII characters or values between A and Z, use the isalpha() function instead.
For example, consider the following extended characters:
These guidelines are covered in the following Knowledge Base (KB) article:
The following KB article discusses methods of handling literal strings:
Please let me know if you have come across any of these issues.