keywords: i18n, internationalization, l10n, localization
In a nutshell, Apple‘s model for localizing a UI is to use .lproj directories and localize your xib files for each language. This way the UI can be tweaked for sizing to make sure everything fits, including changing layouts as needed for the locales where the normal layout just doesn’t work (Apple's Localization Documentation). At the moment (August 2009), the tree has 21 xib files and the GRD files list 50 languages; that means maintaining and shipping over 1000 nib files! A tool could be written to automate pulling the strings from the pak files and putting them into the xib files, but that still leaves the sizing/layout adjustments. Within Google, there are projects that automate driving every part of a product's UI, in every language, and taking screen shots. Someone could inspect all of these, tweak the layouts to get a working UI; but to keep the UI working, there would need to be automation to continually do this to detect when any UI changes, and manually re-inspect each time any part of the UI changes to catch new layout problems, but that means signing up for that work plus the shipping overhead of the nibs.
Some of the cross platform model code already generates content for the UI out of pak files. We‘re following that model with our xib based UI, and doing as much as possible at runtime. The nibs compiled out of xibs are landing in the Resources directory of the app directly, ie-they aren’t language specific. The actual strings in them are markers so a helper can fetch the right strings from the pak files. We've also got some helpers that will do their best to tweak the sizes of objects based on the new string values. The good news is this looks like it is going to be able to cover a lot of the needs we have; but it also means, if we really have to, we can also fall back to creating a locale specific xib for any specific locales that need in (put it in the right .lproj subfolder and Cocoa will pick that up over the more generic one).
These next sections cover some of what is available and how to use it for making sure any Mac UI being built is localized.
Since the UI is similar on all platforms there is a good chance the string you need already exists in the GRD files, but make sure you are using it from the right context. Sometimes multiple IDS_* values have the same English text, in many cases these string values won't be the same in all languages. To figure out the right one to use, read the description to see how they are intended to be used (e.g., one might be a menu item, and another a window title). The differences are usually things like one use is as a Noun (a title, label) and the other is a Verb/Action (menu item, button)–so using the right one is important.
If you do need to add new constants to generated_resources.grd, make sure both the IDS_* value and the description on it are clear. When it reaches the Translation Console they will only have the description to understand what it is/how it‘s used; and if someone is looking at a code/XIB, they might only have the IDS_* value to go by. Remember to put the new entry into a conditional block so it is only defined on the Mac, we don’t want to cause other platforms to carry strings they don‘t actually need. There doesn’t appear to be a solid convention on the naming of the IDS_* constants, but TVL (following what Pinkerton did) has been putting _MAC at the end of the new ones he‘s created to help make it clear they are specific to one platform; it can’t hurt to follow this convention.
Chrome already includes some helpers for fetching resource strings and assembling them (src/ui/base/l10n/l10n_util.h). There are some Mac additions (src/ui/base/l10n/l10n_util_mac.h) to help get things into the form needed for Cocoa UI:
The one to remember here is l10n_util::FixUpWindowsStyleLabel(). If you are getting a list of menu items, button title, etc. from cross platform code, there is a good chance you will need to run it though there before putting it into Cocoa UI objects.
Example: See src/chrome/browser/tab_contents/render_view_context_menu_mac.mm.
The l10n support is built on GTM's UILocalizer and UILocalizerAndLayoutTweaker. For more detail than the steps below, please see GTM's docs and source.
For places where you need to manually load a menu from a XIB file (like the menubar or the menus for the bookmark bar), build the menus in the XIB file like normal, then once you have them wired up go back back and make it localizable as follows:
That's it. During the awakeFromNib the Localizer will run through any referenced object and change any strings that started with ^IDS_ to the values that are looked up.
Examples: See src/chrome/app/nibs/BookmarkBar.xib, src/chrome/app/nibs/MainMenu.xib
Even for the most basic of UIs, like the bookmark editor or new bookmark folder, it can get complicated as the names of labels and button can change with different languages. Just how much work you need to do depends on how complex your UI is.
If you are very lucky, you just need strings replaced and nothing will ever get clipped, then...
Build your UI as you normally would and get things working, then (and these are very similar to what menu's listed):
Example: See ???
If you have a UI with labeled text fields and/or checkboxes/radios, then odds are you need some of the object sizes to be tweaked so text isn‘t truncated and the clickable areas don’t extend miles to the right. GTM's UILocalizerAndLayoutTweaker can handle some of this for you. The GTM docs include a page with examples showing some of what can be done, GTM's UILocalization docs.
Build your UI as normal, then:
Example: See src/chrome/app/nibs/BookmarkEditor.xib.
Notes for the curious:
Preferences was sorta the best example of this, but that has since moved to domui, so the relevant info isn't in the tree any more. If you are curious, you can look at the history to dig up the sources/xibs to see how it was done.
There are cases where the data that ends up being displayed is dynamic, in that it includes data that comes from webpages, servers, etc. For these, we will probably always need to do custom code to help with the layout.
Examples: See src/chrome/browser/cocoa/infobar*.