26 August 2008

Localization and Internationalization

With a co-worker, I recently had the chance to investigate how the Mac OS localization system works. We were curious to see how difficult it would be to convert several interdependent projects (frameworks and applications) to use the suggested ISO language designators (e.g. en.lproj, es.lproj) from the deprecated language designators (e.g. English.lproj, Spanish.lproj). Recorded below are some of the lessons we learned.

The Set Up

According to the Internationalization Programming Topics (applicable page), the old language designators (English.lproj, Spanish.lproj, German.lproj, etc) are deprecated, and could be discontinued in the future. The documentation strongly suggests using the ISO language designators (en.lproj, es.lproj, de.lproj, etc). Judging from Apple's own applications, the OS will be supporting the legacy designators for some time to come. Still, we want to be up to date so it was a little surprising to see that Xcode seems to favor these legacy language designators rather than the ISO labels. The default application template includes an English.lproj directory and sets the Development Region to be English in the Info.plist.

Still, Xcode makes adding new localizations pretty easy. Select the resource that you would like to localize in the file Group Tree and then Get Info. At the bottom of the General tab, there is an "Add Localization" button. When you click on it a sheet appears with a combo box that you can enter the name of the localization. Again, Xcode give examples in the combo box of the deprecated language designators, but it is easy enough to type in the two letter ISO language designator. Once you do this, a new lproj directory is created and a copy of the resource is placed in it.

We played around a bit with a simple application that simply had an image well and a text field on a window. We created several localizations and put custom text and custom images into the text field and image well, respectively, for each locale. Then, we built the application and started moving things around to see what the localization system would do.

Test One, Removing .lproj Directories

Without describing all of the permutations we went through, here's the final experiment we performed. In the Contents/Resources directory, we had the following lproj directories: English.lproj, en.lproj, Spanish.lproj, es.lproj, German.lproj, and de.lproj. In the System Preferences, in the International Pane, we set our first three Languages to be German, then Spanish, then English.

First we opened the application and it showed we were using the resources in the de.lproj directory. We renamed the de.lproj directory and then relaunched the application. The German.lproj directory was used. After commenting out German.lproj, the es.lproj was used, then the Spanish.lproj, then en.lproj, and finally English.lproj. There were two lessons learned here. First, the ISO names are preferred and the language search order in the System Preferences is honored.

Test Two, Removing Resources

Next, we wanted to see what would happen if we deleted resources from within the lproj directories. Our thought from the above experiment was that it would follow the same search path until it found the resource in a localized directory. As we discovered above, when we removed an entire lproj directory, it would fall back to the next most appropriate language. However, if we only removed a resource within an lproj directory, it wouldn't fall back to the next appropriate language, but it would fall back to the English.lproj. Why was it favoring English.lproj, when we had en.lproj and it seemed to favor en.lproj in our first test? The answer is that once the appropriate lproj is found, all of the others are ignored, with the one exception being the lproj designated as the Development Region in the Info.plist.

As described in The Bundle Programming Guide (relevant section) the search for resources goes something like this:

  1. Look for a non-localized verison of the resource.
  2. If that doesn't work, look in the lproj folder that the system tells you to use.
  3. If that doesn't work, look in the lproj that has been designated as the Development Region in the Info.plist.

Sure enough, along with the default English.lproj that Xcode creates for you, it also sets up English as the Development Region (see the CFBundleDevelopmentRegion key in the Info.plist). Once we change the value of that key to en the application started using en.lproj as the fallback for missing localized resources. So the lesson learned with this test is If it can't find the resources, it will fall back to the Development region lproj.

Summary

So, with these experiments under our belts, we feel much more comfortable about converting to the ISO names. We just need to remember:

  • The ISO names are preferred. Names like English.lproj are deprecated.
  • ISO names are searched before deprecated names when the system selects the appropriate lproj.
  • If the system can't find the appropriate lproj, it will search for other languages in the order specified in the International Preference Pane.
  • If a resource isn't found in the selected lproj, it falls back to the lproj designated as the development region with the CFBundleDevelopmentRegion in the Info.plist.

This is a sophisticated system of localization that is predictable and flexible, when you know what is going on. Hopefully, after reading this, you do!

No comments: