I’m now turning my attention to internationalizing my application and framework, and customizing it for various other clients.
EDIT: A first question?
I realized after I wrote this that I have a simple question I don’t see how to solve - how do I know what language a user’s system is set to?
Translation by string keys considered harmful?
Unfortunately, there’s a fundamental issue with Juce’s internationalization scheme which is making it hard to achieve my targets.
If I’m reading the documentation correctly, you tag string that are embedded into your code with a macro called TRANS that marks this text as needing translation into other languages, and then you provide a dictionary to look up these other languages from the original strings.
Unfortunately, this won’t work for anything other than fairly small applications.
The big issue is the assumption that there is a 1:1 correspondence between your small sentence fragments in one language and another. But in fact that’s simply not the case. English turns out to be particularly bad as a root language to translate from this way because it doesn’t have gender or case and fairly often a noun and a similar verb are spelled exactly the same - so you get text that’s grammatical in English but not in the target language.
This problem is exacerbated if you are trying to customize code with your internationalization framework. Now, if you did your framework right, this should be trivial - you create a new special language for each new client you have and perform a translation - but give up on a one-to-one translation from one to the other!
Or consider what happens if you change the wording of text in your code slightly? Suddenly your whole translation no longer works. But perhaps it’s only a tiny change, and you simply don’t want to change the translation? Or, you do want to change the translation sooner or later, but you want to put this change into production this week, and it takes several weeks to get translations done?
Even worse from my perspective, it puts the engineer right in the middle of the workflow. Do you need to change some text in a window somewhere? Call the engineer and he can change the code and rebuild it for you!
How it should be done.
I’ve been involved with this before in organizations, even a very large organization, and there’s only really one way to do it as far as I know.
Each string or string fragment that the user sees needs to have a unique named integer token. An engineer creates a new string to be translated or customized in code by creating a new token, and attaching it in his code to a “placeholder” string - a value that will be displayed if everything else fail (in other words, the value the engineer sees in the early parts of development). The token ID is what is permanent - all translations and customizations are keyed off that.
Either “translating” or “customizing” the code can be done by simply “handing the file of tokens to a translator or a business guy”. Because it’s all data-driven, it’s really easy to edit the labels “in-place” by actually double-clicking on form elements and changing them in the application itself (when you run the app in “editor” mode, of course… :-D)
The big advantage is that the engineer is totally out of the daily workflow loop - they just create the strings and token. and no further work is expected of them, they can later change the “placeholder” name and nothing goes wrong - the one thing you can’t do is change the tokens once created but that’s a standard sort of engineering constraint. Someone else in the business office can customize this for a company, or someone in Indonesia can translate this and generate their own file and the engineer doesn’t even know that this has happened!
How could Juce be changed to do this?
The code would change very little, and I believe that the old and new code could coexist during the transition.
// Old mechanism. Label text1(TRANS("times")); // As in "seven times seven", or "a list of times" or "how many times"? Label text1(TRANS2("times", MULTIPLICATION_BUTTON_LABEL)); // In French: "fois" Label text2(TRANS2("times", ARRIVAL_TIME_CAPTION)); // In French: "heure" Label text3(TRANS2("times", REPETITION_COUNT_LABEL)); // In French: "répétitions"