Recent Posts
- Productivity Nirvana
- Company Love
- No Nike+ for iPhone (e71 musings)
- Olympic Medal Map
- The Fold
- From Research to Personas
What’s In The Loop
- Nokia Mobile
- Reading Online
- UX Pioneers
- Am I Seeing Python Everywhere?
- Lazy mom pushes stroller while on a Segway
- The antithesis of usability...
- Secretary with lightning quick hands
- Cooper | Insights | Journal of Design | Articles | About Face 3: Foreword
- Orange Cone: An unusual addition to the UI collection
- National Design Awards | Cooper-Hewitt, National Design Museum
- Read More…
Archives
Categories
RSS – The Loop
The Loop
Widget Localization
Sundial taught me how to localize Dashboard widgets. It’s pretty painless, really. If you’re not doing it already, hopefully this article will inspire you.
Easier Than You Might Think
Before developing Sundial, the closest I had come to localizing a widget was making it so my PHP Function Reference widget could read all of the available language versions of the PHP documentation. Fully localizing Sundial was actually easier.
Conceptually, the process is pretty simple: replace all strings in your widget with calls to a JavaScript function that pulls the correct string from an associative array populated based on the user’s language preferences as set in the International pane of her System Preferences.
In Mac OS X, users set up a cascade of languages by reordering the list in the International preference pane. Whichever language is listed first is the one the system looks for first when opening a new application. If resources for that language are not found, the system looks for resources related to the next language in the list, and so on. This applies to widgets as well, provided they are organized and programmed in a certain way.
Organizing Your Widget’s Structure
The first thing I did was set up Sundial’s folder structure to contain xx.lproj folders, at the root of the widget bundle, for each langauge into which the widget would be localized (where “xx” is replaced with the language’s two-letter code). Sundial 1.0 shipped with English and French localizations, so it contained en.lproj and fr.lproj folders as shown in this screenshot from my skEdit project view.
You’ll notice four files in my xx.lproj folders: Info.plist, LocalizedStrings.js, and two image files. It is possible to localize any subset of the strings in your widget’s Info.plist file, all of the strings in your widget’s interface (of course), and even images. In the case of Sundial, the “Save Entry” button is actually an image, so we include both French and English versions here.
Apple recommends localizing the name of your widget, if possible, so we do this in fr.lproj/Info.plist by setting CFBundleDisplayName and CFBundleName to “Cadran Solaire” (we don’t bother redefining the other keys because they’re mostly version numbers and such).
LocalizedStrings.js is where most of the heavy lifting occurs. (To keep things simple, this file—like all of your widget files—should be encoded as UTF-8.) LocalizedStrings.js populates an associative JavaScript array with a series of keys and values. The keys consist of the default (in our case English) text for all of Sundial’s strings. The values are the translated versions of the strings.
Using the full strings as the keys in the LocalizedStrings array is quite clever, actually. The JavaScript function (shown below) used to pull the strings is designed to return the key if a related value is not found. That way, you’re never left with a textual hole in your interface. Keeping all of your strings in one single-purpose file also keeps things simple for your translators.
One thing that wasn’t clear to me from Apple’s documentation was how I was supposed to include the LocalizedStrings.js file into my widget. After some experimentation, I figured out that this is how it’s done:
<script type="text/javascript" src="LocalizedStrings.js" charset="utf-8">
</script>
Notice I don’t specify the full path to the file (e.g., en.lproj/LocalizedStrings.js). This is where the magic happens. By not specifying the path, I leave it up to OS X to find the file corresponding to my language settings. Slick.
Ditto with images. To localize an image, don’t specify the full path to it. I found it a little counter-intuitive at first, but quickly got over it.
<img src="logtime_button.png" alt="" />
Something Borrowed, Something Blue
I “borrowed” the following JavaScript localization function straight from Apple’s tutorial on the subject:
function getLocalizedString(key) {
try {
var ret = LocalizedStrings[key];
if (ret === undefined) ret = key;
return ret;
} catch (ex) {}
return key;
}
Here’s a breakdown of what getLocalizedString() does:
- Take a string as the
keyargument - Look in
LocalizedStringsfor an entry matchingkey– this is where OS X reads your language preferences and searches theLocalizedStringsarray in your correspondingxx.lprojfolders until it finds the proper string to return - If a match is found, return it
- Otherwise, return
key - If something goes horribly wrong (that’s the
try/catchpart of it), returnkey
Displaying Localized Strings in Your Interface
Now we have most of the pieces in place to localize our widget. The only thing that’s left is to actually pull the strings defined in LocalizedStrings.js into our interface.
Displaying a particular string is as simple as calling getLocalizedString() and putting the result in the innerHTML or value property of the element. Here’s an example:
HTML Snippet:
<div id="status"></div>
Corresponding JavaScript:
document.getElementById('status').innerHTML = getLocalizedString('Status: Ready');
If your language preferences are set to English, you’ll see “Status: Ready” in Sundial’s status bar. If your language preferences are set to French, you’ll see “Statut: Prêt”. It’s that easy!
In Sundial, I defined a string intialization function that is called when the widget loads. This string goes through all the spots in the interface with text and calls getLocalizedString() on them. Then, whenever I change text in response to a user action, I just use getLocalizedString() to replace the old text with the new.
The Hardest Part
The hardest part of localization is managing the added complexity of maintaining n versions of all of your strings. In Sundial, we got our feet wet by including just two languages at the launch. The next version will include between three and eight more! In our case, no one on our staff is bilingual enough to translate a technical interface, so we are working with different individuals for every language version. By nature, that slows things down a bit as we wait for nearly a dozen people to find (volunteer!) time to respond to requests for additional strings as we add them.
In Conclusion
If you want your widgets to have broad (international) appeal, the extra effort of localization is worth it. And, truth be told, it’s not that much more work. The main thing is to approach the project from the beginning with localization in mind. I hope this article has made you, widget developer, a little more likely to include localization as a feature in your next widget!
Share any experiences or tips you might have about widget localization in the comments below!
Trackbacks
Trackback specific URI for this entryComments
Great tutorial and write-up about widget localization. I have added a link back from the show blog also! Thanks again this is amazing and I am sure that everyone will find it useful. I will post more here once I get my feet wet and need assistance! Thanks again.
Jon
Leave a comment?



Sundial 1.1 is now available for download. This version adds support for several new languages, fixes a couple of minor bugs, and adds a few tweaks to the user interface. Enjoy!The Importance of Localization I talked in a recent blog post about how to
Tracked: Oct 16, 11:55