Tuesday 22 April 2014

Handling characters from any language in preferences used by Firefox extensions

Since Easy Copy allows you to enter any template you want to, you should be able to enter characters in your own language. So whether you use Russian, Japanese or Chinese characters, these should be displayed, copied, stored and retrieved correctly.

Easy Copy stores and retrieves template preferences manually and the Firefox preference tutorials don't mention that you have to handle these characters specially. Even if you use <prefpane> and <preferences> to store preferences, you still need to read them back in a special way.

Easy Copy initially didn't handle this correctly so users' preferences were getting garbled (fixed in v2.0.1). For example, the following template uses the Russian word шеллы (apparently means shells):
URL: %url% and шеллы
would end up looking something like:
URL: %url% and H5;;K
Not quite what you expected. Below I'll describe an easy trick that will allow you to handle all characters correctly.

Encode/decode the template

The problem is that nsIPrefBranch works with UTF-8 characters whereas we're actually working with string in UTF-16 in JavaScript. The trick to making it work is described in Firefox's advanced documentation on reading and writing preferences (easy when you find it).

Whenever you write a user-defined string to a preference, you have to convert it from UTF-16 to UTF-8 using unescape() and encodeURIComponent():
value = unescape(encodeURIComponent(value));
preferences.setCharPref(preferenceKey, value);
Whenever you read a user-defined string from a preference, do the reverse to go from UTF-8 to UTF-16:
var value = preferences.getCharPref(preferenceKey);
value = decodeURIComponent(escape(value));
It's obviously a good idea to wrap this in a utils method or some other object that stores preferences so you can just call that method to do the encoding/decoding for you.

When do you need to use this?

Here are some scenarios where you do and don't need to use it:
  • You allow users to enter text to be stored that could contain any characters, ie. you don't restrict and validate characters.
  • The preference is meant to store characters (ie. a string). If you only store a boolean or integer by using setIntPref() and setBoolPref() then you don't need to use this.
  • If your preferences all use <prefpane> and include a <preferences> section, then Firefox will store these characters correctly. However, you still need to read them back using the decoding trick.
  • You don't need to do this if you use setCharPref() to store an enum of some kind, eg. if you have a dropdown list and want to store the option's key, then it's up to you which characters to use so you can use standard characters.
This took me a while to uncover so I hope it saves someone a couple of hours at least.

No comments:

Post a Comment