Focus on Localization

Localization is like build systems, something that most developers prefer not to think about.
Unfortunately, the developer must take explicit steps to manually externalize all user-visible
strings for the software to be localizable. The localizable strings go into a separate file and
the code references them by a key. The developer must come up with a key and then must manage the
list of externalized strings so that it stays in sync with the code. Some tools have been developed
to make this a little easier, but two types of problems remain very common:

Strings that should be externalized are not. It’s too easy for the developer to put the string
directly into code and then forget to externalize it later.

The string resource files get out of sync with code. The case where the resource file is
missing a string is easy enough to catch at runtime. The case where resource files contain
orphaned strings not referenced in code is much harder to detect.

Since Sapphire is a UI framework, localization is very important. Since Sapphire is focused on
ease of use and developer productivity, relying on current methods of localization is not
satisfactory.

Localizable strings largely occur in two places in Sapphire. You see them in the model
annotations (such as the @Label annotation) and you see them throughout the UI definition files.
Sapphire’s approach is to allow the developer to leave the strings in their original language at
point of use. The string resource files that will be translated are created at build time. The
build system takes the original string and applies a function to it to generate a key for the
string resources file. At runtime, the system loads the original string resources file along
with the localized version and matches original strings to translations by using the
resource keys. Once matching is completed, the resource keys are discarded from memory.

The critical concept is that the developer does not take any explicit steps to enable
localization. It just happens under the covers.

The nature of the function that is used to derive the string resources file key is not
particularly important as long as the resulting key is not overly long, is free
from collisions and doesn't change if the string itself hasn't changed (important for
re-using prior work of translators). The current implementation uses SHA-256 algorithm. It is
important to note that the key is only computed at build-time, thus the performance of the
key creation algorith is not particularly important.

On top of the automatic externalization, Sapphire is architected to minimize the number of
strings that must be externalized in the first place. In particular, when the developer specifies
a property label, the string is expected to be all in lower case (except where acronyms or proper
nouns are used). Sapphire is able to transform the capitalization of the label to make it suitable
for different contexts. Three modes of capitalization are supported:

NO_CAPS: Basically the original string as specified by developer. This is most frequently
used for embedding inside validation messages.

FIRST_WORD_ONLY: This is your typical label in the UI. The colon is added by the UI renderer
where appropriate.

TITLE_STYLE: This is typically used in column headers, section headers, dialog titles, etc.

The current capitalization algorithm works well for English and reasonably well for other
languages, but it will need to be made pluggable in the future.