| |
| |
| As we hinted at the beginning of this chapter, by default Wicket provides a very flexible algorithm to locate the resource bundles available for a given component. In this paragraph we will learn how this default lookup algorithm works and which options it offers to manage our bundle files. |
| |
| h3. Localizing pages and panels |
| |
| Similarly to application class, also component classes can have their own bundle files having as base name the class name of the related component and placed in the same package. So for example if class CustomPanel is a custom panel we created, we can provide it with a default bundle file called CustomPanel.properties containing the textual resources used by this panel. This rule applies to page classes as well: |
| |
| !page-and-panel-bundle.png! |
| |
| One fundamental thing to keep in mind when we work with these kinds of bundles is that the lookup algorithm gives priority to the bundles of the containers of the component that is requesting a localized resource. The more a container is higher in the hierarchy, the bigger is its priority over the other components. This mechanism was made to allow containers to overwrite resources used by children components. As a consequence the values inside the resource bundle of a page will have the priority over the other values with the same key defined in the bundles of children components. |
| |
| To better grasp this concept let's consider the component hierarchy depicted in the following picture: |
| |
| !custom-panel-bundle.png! |
| |
| If CustomPanel tries to retrieve the string resource having 'message' as key, it will get the value 'Wellcome!' and not the one defined inside its own bundle file. |
| |
| The default message-lookup algorithm is not limited to component hierarchy but it also includes the class hierarchy of every component visited in the search strategy described so far. This makes bundle files inheritable, just like markup files. When the hierarchy of a container component is explored, any ancestor has the priority over children components. Consider for example the hierarchy in the following picture: |
| |
| !custom-panel-bundle2.png! |
| |
| Similarly to the previous example, the bundle owned by CustomPanel is overwritten by the bundle of page class BasePage (which has been inherited by CustomPage). |
| |
| h3. Component-specific resources |
| |
| In order to make a resource specific for a given child component, we can prefix the message key with the id of the desired component. Consider for example the following code and bundle of a generic page: |
| |
| Page code: |
| |
| {code} |
| add(new Label("label",new ResourceModel("labelValue"))); |
| add(new Label("anotherLabel",new ResourceModel("labelValue"))); |
| {code} |
| |
| Page bundle: |
| |
| {code} |
| labelValue=Default value |
| anotherLabel.labelValue=Value for anotherLabel |
| {code} |
| |
| Label with id anotherLabel will display the value 'Value for anotherLabel' while label label will display 'Default value'. In a similar fashion, parent containers can specify a resource for a nested child component prepending also its relative path (the path is dot-separated): |
| |
| Page code: |
| |
| {code} |
| Form form = new Form("form"); |
| form.add(new Label("anotherLabel",new ResourceModel("labelValue"))); |
| add(form); |
| {code} |
| |
| Page bundle: |
| |
| {code} |
| labelValue=Default value |
| anotherLabel.labelValue=Value for anotherLabel |
| form.anotherLabel.labelValue=Value for anotherLabel inside form |
| {code} |
| |
| With the code and the bundle above, the label inside the form will display the value 'Value for anotherLabel inside form'. |
| |
| h3. Package bundles |
| |
| If no one of the previous steps can find a resource for the given key, the algorithm will look for package bundles. These bundles have @wicket-package@ as base name and they can be placed in one of the package of our application: |
| |
| !package-bundles.png! |
| |
| Packages are traversed starting from the one containing the component requesting for a resource and going up to the root package. |
| |
| h3. Bundles for feedback messages |
| |
| The algorithm described so far applies to feedback messages as well. In case of validation errors, the component that has caused the error will be considered as the component which the string resource is relative to. Furthermore, just like application class and components, validators can have their own bundles placed next to their class and having as base name their class name. This allows us to distribute validators along with the messages they use to report errors: |
| |
| !validator-with-bundle.png! |
| |
| Validator's resource bundles have the lowest priority in the lookup algorithm. They can be overwritten by resource bundles of components, packages and application class. |
| |
| h3. Extending the default lookup algorithm |
| |
| Wicket implements the default lookup algorithm using the strategy pattern. The concrete strategies are abstracted with the interface @org.apache.wicket.resource.loader.IStringResourceLoader@. By default Wicket uses the following implementations of @IStringResourceLoader@ (sorted by execution order): |
| |
| # *ComponentStringResourceLoader:* implements most of the default algorithm. It searches for a given resource across bundles from the container hierarchy, from class hierarchy and from the given component. |
| # *PackageStringResourceLoader:* searches into package bundles. |
| # *ClassStringResourceLoader:* searches into bundles of a given class. By default the target class is the application class. |
| # *ValidatorStringResourceLoader:* searches for resources into validator's bundles. A list of validators is provided by the form component that failed validation. |
| # *InitializerStringResourceLoader:* this resource allows internationalization to interact with the initialization mechanism of the framework that will be illustrated in [paragraph 18.3|guide:advanced_3]. |
| |
| Developer can customize lookup algorithm removing default resource loaders or adding custom implementations to the list of the resource loaders in use. This task can be accomplished using method getStringResourceLoaders of setting class @org.apache.wicket.settings.ResourceSettings@: |
| |
| {code} |
| @Override |
| public void init() |
| { |
| super.init(); |
| //retrieve ResourceSettings and then the list of resource loaders |
| List<IStringResourceLoader> resourceLoaders = getResourceSettings(). |
| getStringResourceLoaders(); |
| //customize the list... |
| {code} |