My favorites | English | Sign in

Faster apps faster - GWT 2.0 with Speed Tracer New!

Google Web Toolkit

Internationalization & Accessibility

  1. Internationalization
  2. Static String Internationalization
  3. Dynamic String Internationalization
  4. Specifying a Locale
  5. Localized Properties Files
  6. Accessibility Support
  7. Making Widgets Accessible

Internationalization

GWT includes a flexible set of tools to help you internationalize your applications and libraries. GWT internationalization support provides a variety of techniques to internationalize strings, typed values, and classes.

Quick Start with Internationalization

GWT supports a variety of ways of internationalizing your code. Start by researching which approach best matches your development requirements.

  • Do you want to internationalize mostly settings or end-user messages?
    If you have mostly settings and interface labels with fixed text, consider Constants, which uses the least runtime resources. If you have a lot a of end-user messages where you want to add arguments to each message, then Messages is probably what you want.
  • Do you have existing localized properties files you'd like to reuse?
    The i18nCreator tool can automatically generate interfaces that extend either Constants, ConstantsWithLookup or Messages.
  • Are you adding GWT functionality to an existing web application that already has a localization process defined?
    Dictionary will help you interoperate with existing pages without requiring you to use GWT's concept of locale.
  • Do you really just want a simple way to get properties files down to the client regardless of localization?
    You can do that, too. Try using Constants without specifying a locale.

Internationalization Techniques

GWT offers multiple internationalization techniques to afford maximum flexibility to GWT developers and to make it possible to design for efficiency, maintainability, flexibility, and interoperability in whichever combinations are most useful.

  • Extending or implementing Localizable
    Provides a method for internationalizing sets of algorithms using locale-sensitive type substitution. This is an advanced technique that you probably will not need to use directly, although it is useful for implementing complex internationalized libraries. For details on this technique, see the Localizable class documentation.

The I18N Module

Core types related to internationalization:

  • Constants Useful for localizing typed constant values
  • Messages Useful for localizing messages requiring arguments
  • Dictionary Useful when adding a GWT module to existing localized web pages
  • Localizable Useful for localizing algorithms encapsulated in a class

The GWT internationalization types reside in the com.google.gwt.i18n package. To use any of these types, your module must inherit from the I18N module (com.google.gwt.i18n.I18N).

<module>
  <inherits name="com.google.gwt.i18n.I18N"/>
</module>

As of GWT 1.5, the User module (com.google.gwt.user.User) inherits the I18N module. So if your project's module XML file inherits the User module (which generally it does), it does not need to specify explicitly an inherit for the I18N module.

Static String Internationalization

Static String Internationalization is the most efficient way to localize your application for different locales in terms of runtime performance. This approach is called "static" because it refers to creating tags that are matched up with human readable strings at compile time. At compile time, mappings between tags and strings are created for all languages defined in the module. The module startup sequence maps the appropriate implementation based on the locale setting using deferred binding.

Static string localization relies on code generation from standard Java properties files. GWT supports static string localization through two tag interfaces (that is, interfaces having no methods that represent a functionality contract) and a code generation library to generate implementations of those interfaces.

Extending the Constants Interface

This example will walk through the process of creating a class of internationalized constant strings "hello, world" and "goodbye, world" in your GWT application. The example will create a Java interface named MyConstants that abstracts those strings. You can reference the MyConstants methods in your GWT code when you want to display one of those strings to the user and they will be translated appropriately for all locales that have matching .properties files.

Begin by creating a default properties file called MyConstants.properties in your GWT project. You can place the file anywhere in your module's source path, but the .properties file and corresponding interface must be in the same package. It's fine to place the file in the same package as your module's entry point class:

helloWorld = hello, world
goodbyeWorld = goodbye, world

You can also create a localized translation for each supported locale in separate properties files. The properties file must be named the same as our interface name, in our case MyConstants, with the appropriate suffix that indicates the locale setting. In this case, we localize for spanish using the filename MyConstants_es.properties:

helloWorld = hola, mundo
goodbyeWorld = adiós, mundo

Now define an interface that abstracts those strings by extending the built-in Constants interface. Create a new Java interface in the same package where the .properties files were created. The method names must match the tag names uses in the .properties files:

public interface MyConstants extends Constants {
  String helloWorld();
  String goodbyeWorld();
}

Tip: The i18nCreator tool automates the generation of Constants interface subinterfaces like the one above. The tool generates Java code so that you only need to maintain the .properties files. It also works for ConstantsWithLookup and Messages classes.

Note that MyConstants is declared as an interface, so you can not instantiate it directly with new. To use the internationalized constants, you create a Java instance of MyConstants using the GWT.create(Class) facility:

public void useMyConstants() {
  MyConstants myConstants = (MyConstants) GWT.create(MyConstants.class);
  Window.alert(myConstants.helloWorld());
}

Tip: As of the GWT 1.5 release, GWT.create() returns a parameterized object so that the cast of the return value is no longer needed.

You don't need to worry about the Java implementation of your static string classes. Static string initialization uses a deferred binding generator which allows the GWT compiler to take care of automatically generating the Java code necessary to implement your Constants subinterface depending on the locale.

Using the Messages Interface

The Messages interface allows you to subsitute parameters into messages and to even re-order those parameters for different locales as needed. The format of the messages in the properties files follows the specification in Java MessageFormat. The interface you create will contain a java method with parameters matching those specified in the format string.

Here is an example Messages property value:

permissionDenied = Error {0}: User {1} Permission denied.

The following code implements an alert dialog by substituting values into the message:

 public interface ErrorMessages extends Messages {
   String permissionDenied(int errorCode, String username);
 }
 ErrorMessages msgs = GWT.create(ErrorMessages.class)
  
 void permissionDenied(int errorVal, String loginId) {
   Window.alert(msgs.permissionDenied(errorVal, loginId));
 }

Caution: Be advised that the rules for using quotes may be a bit confusing. Refer to the MessageFormat javadoc for more details.

The Benefits of Static String Internationalization

As you can see from the example above, static internationalization relies on a very tight binding between internationalized code and its localized resources. Using explicit method calls in this way has a number of advantages. The GWT compiler can optimize deeply, removing uncalled methods and inlining localized strings -- making generated code as efficient as if the strings had been hard-coded. The value of compile-time checking becomes even more apparent when applied to messages that take multiple arguments. Creating a Java method for each message allows the compiler to check both the number and types of arguments supplied by the calling code against the message template defined in a properties file. For example, attempting to use the following interface and .properties files results in a compile-time error:

public interface ErrorMessages extends Messages {
  String permissionDenied(int errorCode, String username);
}
permissionDenied = Error {0}: User {1} does not have permission to access {2}

An error is returned because the message template in the properties file expects three arguments, while the permissionDenied method can only supply two.

Which Interface to Use?

Here are some guidelines to help choose the right interface for your application's needs:

  • Extend Constants to create a collection of constant values of a variety of types that can be accessed by calling methods (called constant accessors) on an interface. Constant accessors may return a variety of types, including strings, numbers, booleans, and even maps. A compile-time check is done to ensure that the value in a properties file matches the return type declared by its corresponding constant accessor. In other words, if a constant accessor is declared to return an int, its associated property is guaranteed to be a valid int value -- avoiding a potential source of runtime errors.
  • The ConstantsWithLookup interface is identical to Constants except that the interface also includes a method to look up strings by property name, which facilitates dynamic binding to constants by name at runtime. ConstantsWithLookup can sometimes be useful in highly data-driven applications. One caveat: ConstantsWithLookup is less efficient than Constants because the compiler cannot discard unused constant methods, resulting in larger applications.
  • Extend Messages to create a collection of formatted messages that can accept parameters. You might think of the Messages interface as a statically verifiable equivalent of the traditional Java combination of Properties, ResourceBundle, and MessageFormat rolled into a single mechanism.

Properties Files

All of the types above use properties files based on the traditional Java properties file format, although GWT uses an enhanced properties file format that allows for UTF-8 and therefore allows properties files to contain Unicode characters directly.

Dynamic String Internationalization

For existing applications that may not support the GWT locale client property, GWT offers Dynamic String Internationalization to easily integrate GWT internationalization.

The Dictionary class lets your GWT application consume strings supplied by the host HTML page. This approach is convenient if your existing web server has a localization system that you do not wish to integrate with the static string internationalization methods. Instead, simply print your strings within the body of your HTML page as a JavaScript structure, and your GWT application can reference and display them to end users. Since it binds directly to the key/value pairs in the host HTML, whatever they may be, the Dictionary class is not sensitive to the GWT locale setting. Thus, the burden of generating localized strings is on your web server.

Dynamic string localization allows you to look up localized strings defined in a host HTML page at runtime using string-based keys. This approach is typically slower and larger than the static string approach, but does not require application code to be recompiled when messages are altered or the set of locales changes.

Tip: The Dictionary class is completely dynamic, so it provides no static type checking, and invalid keys cannot be checked by the compiler. This is another reason we recommend using static string internationalization where possible.

Specifying a Locale

GWT represents locale as a client property whose value can be set either using a meta tag embedded in the host page or in the query string of the host page's URL. Rather than being supplied by GWT, the set of possible values for the locale client property is entirely a function of your module configuration.

Client Properties and the GWT Compilation Process

Client properties are key/value pairs that can be used to configure GWT modules. User agent, for example, is represented by a client property. Each client property can have any number of values, but all of the values must be enumerable when the GWT compiler runs. GWT modules can define and extend the set of available client properties along with the potential values each property might assume when loaded in an end user's browser using the <extend-property> directive. At compile time, the GWT compiler determines all the possible permutations of a module's client properties, from which it produces multiple compilations. Each compilation is optimized for a different set of client properties and is recorded into a file ending with the suffix .cache.html.

In deployment, the end-user's browser only needs one particular compilation, which is determined by mapping the end user's client properties onto the available compiled permutations. Thus, only the exact code required by the end user is downloaded, no more. By making locale a client property, the standard startup process in <module>.nocache.js chooses the appropriate localized version of an application, providing ease of use, optimized performance, and minimum script size. See the Knowledge Base for more information about the logic of the <modulename>.nocache.js file.

The Default Locale

The com.google.gwt.i18n.I18N module defines only one locale by default, called default. This default locale is used when the locale client property goes unspecified in deployment. The default locale is used internally as a last-resort match between a Localizable interface and a localized resource or class.

Adding Locale Choices to a Module

In any real-world application, you will define at least one locale in addition to the default locale. "Adding a locale" means extending the set of values of the locale client property using the <extend-property> element in your module XML. For example, the following module adds multiple locale values:

<module>
  <inherits name="com.google.gwt.user.User"/>
  <inherits name="com.google.gwt.i18n.I18N"/>
  
  <!-- French language, independent of country -->
  <extend-property name="locale" values="fr"/>

  <!-- French in France -->
  <extend-property name="locale" values="fr_FR"/>

  <!-- French in Canada -->
  <extend-property name="locale" values="fr_CA"/>
  
  <!-- English language, independent of country -->
  <extend-property name="locale" values="en"/>
</module>

Choosing a Locale at Runtime

The locale client property can be specified using either a meta tag or as part of the query string in the host page's URL. If both are specified, the query string takes precedence. To specify the locale client property using a meta tag in the host page, embed a meta tag for gwt:property as follows:

<meta name="gwt:property" content="locale=x_Y">

For example, the following host HTML page sets the locale to "ja_JP":

<html>
  <head>
    <meta name="gwt:property" content="locale=ja_JP">
  </head>
  <body>
    <!-- Load the GWT compiled module code                           -->
    <script src="com.google.gwt.examples.i18n.ColorNameLookupExample.nocache.js " />
  </body>
</html>

To specify the locale client property using a query string, specify a value for the name locale. For example,

http://www.example.org/myapp.html?locale=fr_CA

Tip: The preferred time to explicitly set locale is to do so before your GWT module is invoked. You can change the locale from within your GWT module by adding or changing the locale query string in the current URL and reloading the page. Keep in mind that after reloading the page, your module will restart.

Creating a New Property Provider

If you are embedding your module into an existing application, there may be another way of determining locale that does not lend itself to using the <meta> tag or specifying locale= as a query string. In this case, you could write your own property provider.

A property provider is specified in the module XML file as a JavaScript fragment that will return the value for the named property at runtime. In this case, you would want to define the locale property using a property provider. To see examples of <property-provider> definitions in action, see the files I18N.gwt.xml and UserAgent.gwt.xml in the GWT source code.

Localized Properties Files

Static string internationalization uses traditional Java .properties files to manage translating tags into localized values. These files may be placed into the same package as your main module class. They must be placed in the same package as their corresponding Constants/Messages subinterface definition file.

Tip: Use the i18nCreator script to get started.


 $ i18nCreator -eclipse Foo com.example.foo.client.FooConstants
 Created file src/com/example/foo/client/FooConstants.properties
 Created file FooConstants-i18n.launch
 Created file FooConstants-i18n

Both Constants and Messages use traditional Java properties files, with one notable difference: properties files used with GWT should be encoded as UTF-8 and may contain Unicode characters directly, avoiding the need for native2ascii. See the API documentation for the above interfaces for examples and formatting details. Many thanks to the Tapestry project for solving the problem of reading UTF-8 properties files in Tapestry's LocalizedProperties class.

In order to use internationalized characters, make sure that your host HTML file contains the charset=utf8 content type in the meta tag in the header:

<meta http-equiv="content-type" content="text/html;charset=utf-8" />

You must also insure that all relevant source and .properties files are set to be the UTF-8 charset in your IDE.

Accessibility Support

Screen Readers

A screen reader is an assistive application that interprets what is displayed on the screen for a blind or visually impaired user. Screen readers can interact with the user in a variety of ways, including speaking out loud or even producing a braille output.

Who uses screen readers?

Many people find it helpful to be able to interact with their computers in multiple ways. Though Google does not keep statistics on how many of our users are using screen readers, as the population ages, more people will require assistive technology. It is important to make that our applications are accessible for everybody.

How do screen readers work?

Screen readers listen for a standard set of events that are raised by platform-specific APIs. For example, when an alert window pops up on your screen, Microsoft Windows would expose this event using the Microsoft Active Accessibility API, while a Linux machine would use the Linux Access Toolkit. A screen reader will then communicate this change to the user.

GWT and Screen Readers

Ajax applications are often written in ways that screen readers have difficulty interpreting correctly. A GWT developer writing a tree widget, for example, might use a list element that has been altered to behave like a tree control. But a screen reader would present the control as a list - an incorrect description that renders the tree unusable. Screen readers will also treat HTML span or div elements as regular static text elements, regardless of the presence of JavaScript event handlers for user interaction; you can easily imagine how this causes problems.

ARIA

ARIA is a specification for making rich Internet applications accessible via a standard set of DOM properties. It is currently a work in progress at the W3C. More information can be found at this ARIA page referenced by the Mozilla Developer Center.

Adding accessibility support to GWT widgets involves adding the relevant properties to DOM elements that can be used by browsers to raise events during user interaction. Screen readers can react to these events to represent the function of GWT widgets. The DOM properties specified by ARIA are classified into Roles and States.

The ARIA property role is an indication of the widget type, and therefore describes the way the widget should behave. A role is static and does not change over the lifetime of widget. Some examples of ARIA roles: tree, menubar, menuitem, tab. The ARIA role of a widget is expressed as a DOM attribute named role with its value set to one of the ARIA role strings.

There are also ARIA properties that describe the state of a widget. For example, a checkbox widget could be in the states "checked" or "unchecked". A state is dynamic and should be updated during user interaction. Some examples of ARIA states: aria-disabled, aria-pressed, aria-expanded, aria-haspopup. Note that an ARIA state itself a DOM attribute -- for example, a toggle button widget that wants to express that it has been pressed will have an attribute aria-pressed set to true.

The role of a widget determines the set of states that it supports. A widget with the role of list, for example, would not expose the aria-pressed state.

Also, accessible widgets require keyboard support. Screenreaders will speak the element that has keyboard focus, so keyboard accessibility can be accomplished by moving focus to different elements in response to keyboard commands.

GWT and ARIA

Once ARIA roles and states are added to the DOM of a GWT widget, browsers will raise the appropriate events to the screen reader. As ARIA is still a work in progress, browsers may not raise an event for every ARIA property, and screenreaders may not recognize all of the events being raised.

Many GWT widgets now have keyboard accessibility and ARIA properties. These include CustomButton, Tree, TreeItem, MenuBar, MenuItem, TabBar, and TabPanel. Also, all widgets that inherit from FocusWidget now have a tabindex by default, allowing for better keyboard navigation.

Making Widgets Accessible

This section describes how to add accessibility support to your GWT application and widgets. A variety of different techniques are discussed; there is not yet a standard and generally-applicable approach for making AJAX applications accessible, but we provide some suggestions.

Class com.google.gwt.user.client.ui.Accessibility

The Accessibility class implements the needed accessor and modifier methods for manipulating DOM attributes defined by the ARIA specifications. It also defines constants for the ARIA role and state names which are used by GWT widgets.

Adding ARIA Roles

The ARIA attribute role is an indication of the widget type; it describes the way the widget should behave. Roles are static and should not change during the lifetime of a widget. Widget authors should:

  • Pick the right role for the widget from the list of supported ARIA roles.
  • Set this attribute at construction time.

The following is an example taken from the CustomButton widget. Adding the button role indicates to an assistive technology that the widget will behave like a button.

  protected CustomButton() {
    ...
    // Add a11y role "button". Accessibility.ROLE_BUTTON is the String constant
    // "button"
    Accessibility.setRole(getElement(), "role", Accessibility.ROLE_BUTTON);
    ...
  }

Note that some of the role names have already been defined as constants in the Accessibility class.

Adding ARIA States

An ARIA state is an additional attribute that reflects the current state of a widget, for example, whether a checkbox is checked or unchecked. A state should be initialized at the time a widget is constructed and updated during user interaction.

Note that:

  • A widget can have numerous state attributes, whereas a widget can only ever have one role attribute.
  • State attributes are dynamic and change during the widget's lifetime. A role once set does not change.

Also, some of the relevant state names are defined as constants in the Accessibility class.

Initializing States

Once a specific ARIA role has been associated with a widget, it is important to check which states are associated with that role. For example, the button role has two state attributes:

aria-disabled
Indicates that the widget is present, but is not allowed for user actions.
aria-pressed
Used for buttons that are toggleable to indicate their current pressed state.

The aria-pressed state is not used in the CustomButton widget, as it is not supported by most screen readers yet. However, the code in the example below is the code that we would write when the aria-pressed state is better supported.

In the CustomButton widget, the aria-pressed ARIA state is initialized as follows:

  protected CustomButton() {
    ...
    // Add a11y state "aria-pressed"
    Accessibility.setState(getElement(), "aria-pressed", "false");
  }

Updating States During User Interaction

The CustomButton widget has support for multiple button faces, giving developers more stylistic control. Also, unlike the Button widget, a CustomButton can be toggleable, as is the case with the CustomButton subclass ToggleButton. Event handlers attached to the underlying DOM element update the button faces when the button is pressed. At the same time, the Accessibility.setState(Element, String, String) method should be used to manipulate the DOM and update the value of the ARIA state aria-pressed:

  void toggleDown() {
    ...
    // Update a11y state "aria-pressed"
    Accessibility.setState(getElement(), "aria-pressed", isDown() ? "true" : "false");
  }

It is important to make sure that all event handlers that change the state of the widget also change the ARIA state.

Adding Keyboard Accessibility

Keyboard accessibility is a key requirement when access-enabling GWT widgets. When developing a new widget, ensure that it is keyboard accessible from the outset; adding keyboard accessibility later can be difficult. Screenreaders will speak the element that has keyboard focus, so keyboard accessibility can be accomplished by moving focus to different elements in response to keyboard commands.

Proper keyboard accessibility affords the following end-user behavior:

  • Users can tab to move focus to the widget.
  • When the widget receives focus, the screenreader will interpret the ARIA roles and states that are set on the widget.
  • The screenreader will speak a description of the widget, and its textual content.

By default, the only elements in the HTML DOM that can receive keyboard focus are anchor and form fields. However, setting the DOM property tabIndex (note that this corresponds to HTML attribute tabindex) to 0 will place such elements in the default tab sequence and thereby enable them to receive keyboard focus. Setting tabIndex = -1, while removing the element from the tab sequence, still allows the element to receive keyboard focus programmatically.

In GWT, any widget that extends the FocusWidget abstract class will be keyboard focusable by default. The FocusWidget abstract class includes a setFocus(boolean) method that can be used to programmatically set the focus or remove focus on the widget. FocusWidget also includes a setTabIndex(int) method that allows the user to set the DOM property tabIndex for the widget.

Keep in mind that extending FocusWidget does not guarantee focusability for your widget. The base element of the the FocusWidget (passed to the superclass constructor) must be a naturally focusable HTML element. In the case where the new widget does not have an obviously focusable component, a focusable element can be created via the FocusPanel.impl.createFocusable() static method. Look at CustomButton's implementation for an example of this.

For widgets that don't extend the FocusWidget abstract class, ensuring keyboard accessibility can be more difficult. Different browsers set focus in different ways, and focus on arbitrary elements is not supported everywhere. You can use FocusPanel to enclose elements that need to receive keyboard focus; just be sure to test your widget on different browsers.

For an example of using the tabIndex property, see the MenuBar widget. The root menu is the only one that should be in the tab sequence; its sub-menus are not. To achieve this, the tab index is set to 0 in the MenuBar's constructor, and as new MenuBars are added as sub-menus, their tab indexes are reset to -1.

Indicating Selection Changes to Screen Readers for Items That Are Not Naturally Focusable

Some widgets, such as GWT's Tree and MenuBar widgets, consist of a container with a set of items. The container has a naturally focusable DOM element, but the items themselves do not. The focusable element receives all keyboard input, and causes visual changes in the contained items to indicate a change in item selection. For example, GWT's Tree widget contains TreeItems; both of these elements are made up of divs. However, the Tree also has a naturally focusable hidden element which receives keyboard events. Whenever the user hits the arrow keys, this element handles the event and causes the appropriate TreeItem to be highlighted.

While this technique holds up for sighted users, it plays havoc with screen readers. Since the TreeItems themselves never get natural focus when selected, there is no way for the screen reader to know that the item selection has changed. One possible way to remedy this would be to have each TreeItem be naturally focusable. Unfortunately, TreeItems can contain more than just text -- they can contain other widgets, which themselves can be focusable. Here, delegating focus properly would be fairly complex -- each TreeItem would have to handle all of the key events for its child widget, and decide whether or not to delegate key events to its child (for user interaction with the child widget), or to handle the key events itself (for Tree navigation). Keep in mind that hooking up keyboard event handlers for each item would become unwieldy, as Trees may become very large. One can avoid doing this by relying on the natural event bubbling of key events, and having an element at the root of the Tree widget be responsible for receiving events.

Another way to remedy the situation is to use the ARIA aria-activedescendant state. This state is set on an element that is naturally focusable, and its value is the HTML id of the currently-selected item. Whenever the item changes, the aria-activedescendant value should be updated to the id of the newly-selected item. The screen reader will notice the change in the value and read the element corresponding to the id. Below is an example of how this technique is used on the GWT Tree and TreeItem widgets.

First, we set roles on the Tree's root element and its focusable element:

  // Called from Tree(...) constructor
  private void init(TreeImages images, boolean useLeafImages) {
    
    ...
    
    // Root element of Tree is a div
    setElement(DOM.createDiv());

    ...
    
    // Create naturally-focusable element
    focusable = FocusPanel.impl.createFocusable();

    ...
    
    // Hide element and append it to root div
    DOM.setIntStyleAttribute(focusable, "zIndex", -1);
    DOM.appendChild(getElement(), focusable);

    // Listen for key events on the root element
    sinkEvents(Event.MOUSEEVENTS | Event.ONCLICK | Event.KEYEVENTS);
    
    ...
    
    // Add a11y role "tree" to root element
    Accessibility.setRole(getElement(), Accessibility.ROLE_TREE);
    
    // Add a11y role "treeitem" to focusable element. This is necessary for some screen 
    // readers to interpret the aria-activedescendant state of this element.     
    Accessibility.setRole(focusable, Accessibility.ROLE_TREEITEM);       
 }

Whenever an item selection changes, the value of the aria-activedescendant state is set on the focusable element, and the ARIA states of the currently-selected item are set:

  // Called after a new item has been selected
  private void updateAriaAttributes() {

    // Get the element which contains the text (or widget) content within
    // the currently-selected TreeItem
    Element curSelectionContentElem = curSelection.getContentElem();

    ...
    
    // Set the 'aria-level' state. To do this, we need to compute the level of
    // the currently selected item.    
    Accessibility.setState(curSelectionContentElem, Accessibility.STATE_LEVEL,
        String.valueOf(curSelectionLevel + 1));

    // Set other ARIA states
    ...
    
    // Update the 'aria-activedescendant' state for the focusable element to
    // match the id of the currently selected item.
    
    Accessibility.setState(focusable, Accessibility.STATE_ACTIVEDESCENDANT,
        DOM.getElementAttribute(curSelectionContentElem, "id"));
  }

Though it is not shown in this code snippet, when TreeItems are created, they are constructed out of several divs, only one of which contains the content that we wish to be interpreted by the screen reader. This div is assigned a unique DOM id (which is generated using the DOM.createUniqueId() method), and a role of treeitem. These attributes are not set on the root TreeItem div because it contains a child image, which we do not want to be read.

Caveats with this Approach

The obvious problem with this approach is that unique DOM ids need to be assigned to all of the possible items that could be selected. While this is easy enough to implement, it seems unwieldy to assign a DOM id to each item.

Also, there is a subtle problem with using the aria-activedescendant state. Originally, the intended use-case for this state was the implementation of a listbox with divs. Whenever the aria-activedescendant value of the parent div (which was the one with natural focus) would change, the screen reader would read out the text of the list item with the corresponding id, ignoring any roles or states set on the selected item. This is fine for widgets as simple as a listbox; the selected item has enough text for the user to understand what is selected. However, in the case of a Tree, the selected item's text may not be enough. For example, which level of the tree is the selected item on?

Some screen readers have started to do more than just read the text of items selected with aria-activedescendant, interpreting the item just as they would any other element that received keyboard focus. However, not all screen readers do this yet.

Associating Meaningful Labels

A web page will often include human-readable descriptive elements (such as Label or HTML widgets) that explain the purpose of a particular widget. However, the association between a widget and its description may not be obvious to a browser or a screen reader. ARIA defines the aria-labelledby state which can be used to explicitly associate a widget with one or more descriptive elements.

In order to associate a label with a widget, ensure that descriptive elements all have a unique id. The assigned id can later be used with to set the aria-labelledby state of a widget to refer to the id values of any descriptive elements, thereby associating those descriptive elements with the widget.

Automatically Speaking Highlighted Content

AJAX components often highlight an item of interest without moving keyboard focus to that item. This creates a good end-user experience when using components such as autocomplete widgets; the user can continue to type and obtain further refinements to the available set of choices. Because screenreaders traditionally attempt to speak the item that has keyboard focus, they will not read highlighted items. ARIA live regions help make widgets such as autocomplete boxes usable for visually impaired users.

How It Works

The ARIA role region is used to declare areas that hold such live content, i.e., content that updates dynamically without having keyboard focus. The ARIA state aria-live on such regions specifies the priority of such updates; think of this as a politeness setting. Here is a code example that should provide the general idea of how to implement this technique for an auto-complete widget:

Initialize Live Region

The ARIA role region is added when instantiating the relevant DOM nodes in the AutoCompleteWidget constructor:

  public AutoCompleteWidget() {
    ...
    // Create a hidden div where we store the current item text for a
    // screen reader to speak
    ariaElement = DOM.createDiv();
    DOM.setStyleAttribute(ariaElement, "display", "none");
    Accessibility.setRole(ariaElement, "role", "region");
    Accessibility.setState(ariaElement, "aria-live", "rude");
    DOM.appendChild(getElement(), ariaElement);
  }

Here, we have created a hidden div element that holds the content to be spoken. We've declared it to have role = 'region' and live = 'rude'; the latter setting specifies that updates to this content have the highest priority. Next, we set up the needed associations so that the set of suggestions returned as the user types into the AutoCompleteWidget's text box are put in hidden div:

    // This method is called via a keyboard event handler
    private void showSuggestions(Collection suggestions) {
      if (suggestions.size() > 0) {
    
        // Popupulate the visible suggestion pop-up widget with the new selections
        // and show them
        ....
    
        // Generate the hidden div content based on the suggestions
    
        String hiddenDivText = "Suggestions ";

        for (Suggestion curSuggestion : suggestions) {
          hiddenDivText += " " + curSuggestion.getDisplayString();
        }
    
        DOM.setInnerText(ariaElement, hiddenDivText);
      }
    }

Problems with this Approach

With this technique, the developer has complete control over the text that is spoken by the screen reader. While this seems like a good thing for the developer, it's not great for users of screen readers. Taking this approach, developers of AutoComplete widgets may decide on different text that the screen reader should read. For example, another screen reader might prefix each suggestion with the "Suggestion x", where x is the index of the suggestion in the list. This leads to an inconsistent experience across applications. If both developers were able to make use of ARIA roles and states, then a more consistent experience would result, in accordance with the ARIA specification.

A more direct problem with this approach is internationalization. Most developers would realize that the list of suggestions needs to be translated into different languages; this list is directly displayed on the screen. However, the word 'Suggestions', which is the first word on the live region, could easily be missed, since it is never visually displayed to the user. These sorts of descriptive terms must also be translated. If ARIA roles and states could be used, then translation of the spoken terms associated with the roles and states would be the screenreader's job; developers would only need to be responsible for translating their content.

General Advice For Widget Developers

First and foremost, use native HTML controls whenever possible. Native HTML controls are well understood by screen readers. They do not require ARIA roles and states, which has two main benefits:

  • ARIA is not yet supported by all major browsers. Screen reader and browser developers have already done the work to make HTML controls accessible.
  • Reimplementing native HTML controls using divs (for example) can cause poor performance. For example, suppose a developer were to re-implement a listbox using divs. One of the ARIA states that applies to the listitem role is aria-posinset. This value indicates the position of the item within its parent container, which corresponds to the item's index in the listbox. The problem is that every time an item is added or removed from the listbox, one has to iterate through all of the items in the list, adjusting their aria-posinset values. Though there are some optimizations that can be done, this is still much slower than native HTML select elements.

If native HTML controls cannot be used and a custom widget has to be built, keep in mind that it is much easier to develop an accessible widget from the beginning than to go back and add accessibility support to an existing widget. While adding ARIA roles and states is relatively easy, ensuring that the appropriate elements receive keyboard focus during user interaction can be more complicated.

Also, widgets that subclass other widgets should end up with the appropriate ARIA roles and states. Your superclass widget might already specify a certain ARIA role, and while the Accessibility.setRole(Element, String) method will overwrite a previous ARIA role in the same element, a complicated DOM configuration may result in different ARIA roles being placed in different elements.

Make sure to test that a new widget is accessible! There are three basic steps in the translation between the DOM and the screen reader:

  • DOM: since ARIA attributes are being added directly to the DOM, an easy way to check that the attributes are in the right location is to use a DOM inspector like Firebug
  • Events: it is important to make sure that Firefox raises the appropriate events in response to ARIA attributes, changes in focus, and changes in the widget itself. A Microsoft tool called the Accessible Event Watcher, or AccEvent, can allow you to check which events are being raised.
  • The screen reader: in the end, you can be most sure that your GWT widgets are accessible by using a screen reader. Some screen readers may not be listening for all of the events raised by Firefox, or they might expect the ARIA attributes to be added to the DOM in certain locations. The most widely used screen readers with some support for ARIA are JAWS and Window-Eyes. FireVox, a free text-to-speech add-on for Firefox, also includes support for ARIA.