Ken & Mike on the MATLAB Desktop

May 4th, 2009

Internationalization of GUIs

Recently, Mario asked on the MATLAB newsgroup about how to provide a localized version of his GUI made in GUIDE. This is an interesting problem with no official, but many worthwhile solutions.

Right now depending on where you live you can purchase MATLAB either in English or Japanese:

English and Japanese MATLAB side by side

The translated strings that ship with MATLAB come compiled and bundled up, and we’re not able to extend our translation mechanisms into a tool that would be useable for MATLAB customers. This means we have to think of other ways for providing localizeable GUIs. Here’s an example GUI that I quickly whipped up using a few easily translateable nouns. I apologize ahead of time, the buttons will only work if you have the image processing toolbox as I took some of my favorite images from its demos. However, the language changing menu works without any toolboxes. There’s a lot more code than you’re used to seeing on this blog, so I’ve provided a zip file with all the examples at the end.

The translate GUI

Below I present four of many possible strategies to create an internationalizeable MATLAB GUI. For each of these strategies the actual GUI code (the translateDemo.fig and translateDemo.m files) remain unchanged. The difference happens in the dispatch function getLabel.m, which is called from the following function by the menu callbacks. Note that menu labels have a different property to set than the uicontrols. I’ll leave it as an exercise for you to simplify this function using the UserData or Tag properties.

function setLanguage(lang)
% find the label or string for each of the uicontrols for a given language
% and set it.
h = guidata(gcf);
set(h.language, ‘Label’,  getLabel(lang, ‘language’));
set(h.english,  ‘Label’,  getLabel(lang, ‘english’));
set(h.french,   ‘Label’,  getLabel(lang, ‘french’));
set(h.german,   ‘Label’,  getLabel(lang, ‘german’));
set(h.peppers,  ‘String’, getLabel(lang, ‘peppers’));
set(h.children, ‘String’, getLabel(lang, ‘children’));
set(h.canoe,    ‘String’, getLabel(lang, ‘canoe’));

Using MATLAB classes
The first method makes use of constant properties in MATLAB class objects, allowing us to define the strings using the same pattern as one class file per language. Note that the keys for all of the languages are in English. It doesn’t matter what the keys actually are, as long as they are consistent. As you can see from my getLabel function, I’m taking advantage of the default subsref to access a class property dynamically. I think it’s a neat trick.

getLabel.m

function xlatedString = getLabel(lang, label)
xlatedString = ‘???’;
switch lang
    case ‘en’
        xlatedString = EnglishStrings.(label);
    case ‘de’
        xlatedString = GermanStrings.(label);
    case ‘fr’
        xlatedString = FrenchStrings.(label);
end

The following is my class with the English labels. The French and German ones follow the same pattern.
EnglishStrings.m

classdef EnglishStrings
    properties (Constant)
        language = ‘Language’;
        english = ‘English’;
        french = ‘French’;
        german = ‘German’;
        canoe = ‘Canoe’;
        children = ‘Children’;
        peppers = ‘Peppers’;
    end
end

The advantage of this method is that it takes the MATLAB search path out of the equation. If you can run the GUI code, you can get to the class objects–the file locations do not need to be hard coded.
Using XML
Hard coding the strings in class files may be a little a clunky or not fit into your workflow. The next method makes use of XML files, but you can use whatever combination of files and I/O methods to read them you’d like: plain text files (with fread), Microsoft Excel files (with xlsread), or even using a containers.Map and saving/loading .MAT files!
getLabel.m

function xlatedString = getLabel(lang, label)

xlatedString = ‘???’;
switch lang
    case ‘en’
        xmlfile = ‘english.xml’;
    case ‘de’
        xmlfile = ‘german.xml’;
    case ‘fr’
        xmlfile = ‘french.xml’;
end

try
    xDoc = xmlread(xmlfile);
    root = xDoc.getDocumentElement;
    strings = root.getElementsByTagName(’string’);
    for i=0:strings.getLength - 1
        if strcmp(strings.item(i).getAttribute(‘id’), label)
            %translate from java string to MATLAB string
            xlatedString = char(strings.item(i).getTextContent);
        end
    end
catch %#ok<CTCH>
    %ignore any read errors and use ‘???’
end

french.xml

<?xml version="1.0" encoding="UTF-8"?>
<strings>
    <string id="language">Langue</string>
    <string id="english">Anglais</string>
    <string id="french">Français</string>
    <string id="german">Allemand</string>
    <string id="canoe">Canoë</string>
    <string id="children">Enfants</string>
    <string id="peppers">Poivrons</string>
</strings>

Using a Java Resource Bundle
Java provides a built-in mechanism for storing strings and choosing them based upon locale called a ResourceBundle. We can take advantage of java.util classes from MATLAB so this method uses Java’s solution for our MATLAB GUI. Remember to convert the java.lang.String returned from the bundle using the MATLAB char function.
getLabel.m

function xlatedString = getLabel(lang, label)
%using the java resource bundle
xlatedString = ‘???’;
try
    file = java.io.File([’strings_’ lang ‘.properties’]);
    inputStream = java.io.FileInputStream(file);
    resourceBundle = java.util.PropertyResourceBundle(inputStream);
    xlatedString = char(resourceBundle.getString(label));
catch %#ok<CTCH>
    %ignore any read errors and use ‘???’
end

strings_de.properties

language=Sprache
english=Englisch
french=Französisch
german=Deutsch
canoe=Kanu
children=Kinder
peppers=Paprikas

The Java ResourceBundle mechanism comes with the ability to automatically select the right file based upon the locale information and filename. To take advantage of that, we’d have to discuss how MATLAB and the ClassLoader interact…a topic for a different post.
Google Translation Services

The translate GUI with Spanish added

Thanks to smart people at Google, you can get live translation to/from nearly any language. For this version of the GUI, I’ve added Spanish, Finnish, Filipino, and Turkish to the menu for almost no extra work. All the strings are translated live through Google’s translation API. I won’t interpret their Terms of Service for you, but if you do decide to use their services, play nice: it’s a great service they’re giving away for free.

getLabel.m

function xlatedString = getLabel(lang, label)
xlatedString = ‘???’;
try
    url = ‘http://ajax.googleapis.com/ajax/services/language/translate’;
    %’en’ (English) is the base language
    page = urlread(url,‘get’,{‘v’,‘1.0′,‘q’,label,‘langpair’,[‘en|’ lang]});
    pattern = ‘(?<=”translatedText”:”)(.+?)(?=”)’;
    xlatedString = regexp(page,pattern,‘match’,‘once’);
catch %#ok<CTCH>
    %ignore any read errors and use ‘???’
end

To make this nicer, I should probably look up the stored English strings instead of using my object labels for the translation (i.e. this doesn’t work for a phrase and doesn’t do the correct capitalization). Also, this code can be cleaned up a little using François Glineur’s JSON Parser from the MATLAB file exchange.

The real power here, I think is that you can provide a localized UI without having to develop a competency in that language. Of course the real question is whether or not it is better to provide no translation or a translation that could be wrong and confusing.

Determining Language Automatically
Most modern operating systems know something about the user’s environment such as the location and language. Java is able to get this information from the system and thus you can get it from MATLAB:
java.util.Locale.getDefault().getLanguage()
This returns the cute little two-letter language code that is consistent with the ones I’ve been using, and so we can stick this in the openingFcn function and have out GUI automatically detect the language and choose the appropriate strings.

function translateDemo_OpeningFcn(hObject, eventdata, handles, varargin)
% This function has no output args, see OutputFcn.
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% varargin   command line arguments to translateDemo (see VARARGIN)

% Choose default command line output for translateDemo
handles.output = hObject;

% Update handles structure
guidata(hObject, handles);

%default to the peppers
peppers_Callback(handles.peppers, [], handles);
setLanguage(char(java.util.Locale.getDefault().getLanguage()))

A web search should reveal how to change the language settings for your particular OS. Here’s how to do it for Windows XP, and the result of making my machine think I understand Finnish:

Windows XP Finnish language settings
demo GUI in Finnish

I’d like to close this post asking my high school teachers, colleagues in France and Germany, and Aurélien to forgive me for any embarrasing translation mistakes. Bon Chance!

You can download the demo code right-clicking here and doing a Save As. I will also put them on the MATLAB file exchange and link them once they are up.

2 Responses to “Internationalization of GUIs”

  1. Aurélien Queffurust replied on :

    Hi Mike!
    Thanks for the wink ;)
    Just to let you know that there is no mistake concerning French words in your code ….except your last expression for good luck, we say “bonne chance” instead of “bon chance” (core value #4 -> Learning and Self-Improvement! )

    Aurélien

  2. Matt Whitaker replied on :

    Thanks for the really nice overview of this topic. I like the XML solution for my situation as users could easily develop their own application vocabulary. I’d probably also then have an XML file containing the mapping between the language codes and the vocabulary files.

    I’m interested in seeing the blog about having the Java ResourceBundle’s mechanism to automatically select the right file based upon the locale

    Cheers,
    Matt

Leave a Reply

Wrap code fragments inside <pre> tags, like this:

<pre class="code">
a = magic(3);
sum(a)
</pre>

If you have a "<" character in your code, either follow it with a space or replace it with "&lt;" (including the semicolon).


Ken & Mike work on the MATLAB Desktop team.
  • Ori: The current folder shortcut used to be alt-y. Now it is alt-o. However, while in the editor window, alt-o opens...
  • Jose Miguel: Hi First of all, thanks for the post, it is really useful. I am trying to develop a Java GUI within...
  • Ken: Hi Siddharth, There isn’t currently any way to move the docking controls. Feel free to submit an...
  • Phil: I have the same problem as described above with UITable working fine in the Matlab environment but showing no...
  • Siddharth: Is there any way to move the position of the docking controls (or eliminate them through some...
  • Chris: Yes, it is a challenge to organize functions into categories, especially with so many functions. Ken and I...
  • Mike: Thanks for fielding that one, Yair.
  • Yair Altman: Jimmy - if you mean that you wish to include hyperlinks in your function’s help comment, that will...
  • OysterEngineer: Thanks for explaining the Function Browser. I fired it up and gave it a try. It appears that it has...
  • Jimmy: Is there any way to include hyperlinks in a comment, such as the standard help at the beginning of a function?

These postings are the author's and don't necessarily represent the opinions of The MathWorks.