One frequent request we get is to make it so users can run an application in any language that an app is built for and NOT the one the OS would use (for OS X see this blog post, Windows and Linux aren’t quite so dynamic).
But sometimes we get requests to be able to create an application that can run in a language that is NOT the same one used by the OS.
To do this you could write your own localization system using a database or other resource based files, or you could just use dynamic constants. Of course a lot of you are going to say “but those just pick the OS suggested match” and for the most part you’re correct. Some time ago we added the ability for Xojo to not only get the value based on the language the OS suggested, but also a means to get a specific localized value – one that may NOT be what the OS would recommend.
That’s all well and good. So how do you do this?
First off, it will mean more work for you to NOT do what the OS recommends. Let me be clear, you will have MORE work. But you can do it.
Here’s a simple example. Create a new project, on the default Window create one dynamic constant called “dyValue”.
We’re going to add values to it so you can see this all works without too much trouble as follows:
OS | Language | Value |
Any | German | german |
Any | French | french |
Any | English (UK) | english (uk) |
In a normal application you’d set up every localized string you want to use with all the localized values you want to use.
Now on Window1 if you put a label and, like usual, you set its Text property in the Inspector to #dyValue you get the normal behavior where the text selected will be the OS selected localized value. But since that’s NOT what you want (remember I said there’s more work involved?), you need to override the normal OS behavior.
Conceptually what putting the name of the dynamic constant in the Text field marked with a hashtag does is set the text to the selected localized value in the constructor (not really, but conceptually).
You can kind of get this behavior if in the open event of the control you were to put:
me.Text = dyValue
And since you could do this manually on every control, it would be great if there was a way to have the dynamic constant get the localized value you want instead of just grabbing the one the OS language dictates.
Well, guess what? There is! So how do you do this?
Given the example dynamic constants and the code in the open event to set the text, let’s change that to read:
me.Text = dyValue( “de” )
And hit Run. What does the label say?
OK so what did that one line do?
Well, some time ago we realized that to host web apps and make them localizable you could not rely on the language of the SERVER to send back the right localized values. Your server could be running in English but trying to send back pages to people in French, German or any other language. So the server had to be able to say “get me the localized version of this dynamic constant in this language” and the language might be specified by a user preference in the app or the browser header.
So you can ask a dynamic constant for a specific localization of a given dynamic constant.
Note that to the OS you are still running in whatever language it told you to be running in – you just have a way to select different localized constant values. This is especially important to take note of on OS X as there are a number of spots where trying to override the OS localization will not work – the Application menu will be in whatever language the OS determined it should be, certain menu items that are automatically localized will be in the OS selected language.
Note that for a web server this isn’t an issue as it does not have automatically localized menu items. But for a desktop application it will. So I strongly suggest you not try to override the OS – as you could end up trying to show one language in the things you can affect and whatever language the OS selected in others (and no, it’s not a bug so you’ve been forewarned).
Onwards! So what did that one little change do?
Dynamic constants behave like method calls – in this case we passed a parameter which is the “language code” we wanted the dynamic constant value for. And the dynamic constant will do the best it can to get that value if it exists – if it doesn’t then, garbage in garbage out (although practice seems to suggest it’s the default value for the dynamic constant and not what you would get if you did not pass a language code at all).
So, if you want to provide a way to select a language at runtime:
– Be aware that in a desktop app you can’t override everything (particularly on OS X).
– Create a global property (or however you feel you want to do this) that stores the language code.
– Write code in EVERY control to set the text, caption etc. that uses the dynamic constant with the language code. Remember, I said there’s a lot more work!
– This is all the stuff the IDE does for you automatically when you use the #dynamicConstantName form in the inspector. Oh, and no, we won’t be adding a form to the inspector that lets you write #dynamicConstant( variablename ) so don’t ask!
And now yer good to go! Though, you may have one last question: “How do I know what the language codes are?”
Thankfully, this one is pretty easy. Create all your localized constants then build and look in the Resources directory. For each OS localization will be in an “lproj” directory. en.lproj for english, fr.lproj for french and so on. On Windows and Linux they will be in the .MO files.
There’s a handful that are more than just a two letter language code – English (UK) is both the language and locale code (en-GB) and the two variations of Chinese, simplified (zh_CN) and traditional (zh_TW), Norwegian (with two variations BokmÃ¥l (nb) and Nynorsk (nn).
Have at it, everyone!