Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 2 Next »

Based on a novel by Michael Ketting

The MultiLingualResources attribute and classes

You can specify more than one MultiLingualResources attribute
for a class. Each attribute associates the class with a resource
file.

If you derive more classes from the attributed class, you can
combine the attributes along the line of ancestry. Resources
for sub-classes extend the resources of their base-classes.
Identical resource-IDs in sub-classes override those in the
base-class.

Implementation of IObjectWithResources

The page, or user control, shall implement IObjectWithResources
as follows:

using Remotion.Globalization;

IResourceManager IObjectWithResources.GetResourceManager() {
  return this.GetResourceManager();
}

protected virtual IResourceManager GetResourceManager() {
  Type type = this.GetType();
  if (MultiLingualResourcesAttribute.ExistsResource (type))
    return MultiLingualResourcesAttribute.GetResourceManager (type, true);
  else
    return null;
}

Usually this facility is provided in a common base-page, or a common
base-user control. The derived pages or user controls only the
MultiLingualResources attribute is specified. If a base-class
can be used, the implementation of IObjectWithResources is
stored in the corresponding code-behind files.

Accessing resources

For accessing resources we recommend the ResourceManagerUtility:

using Remotion.Globalization;
using Remotion.Web.UI.Globalization;
IResourceManager rm = ResourceManagerUtility.GetResourceManager (this);

The utility iterates over the control hierarchy, starting at the
control passed as a parameter, going up to the page if necessary.
If IObjectWithResources is implemented as part of the control
or the page, the IResourceManager will be queried and cached. Finally,
the IResourceManager objects of the control hierarchy will be
combined and passed as a single instance of IResourceManager.

Querying a resource entry (identified by Res1 in this
example) works like this:

string valueRes1 = rm.GetString ("Res1");

Defining resource-IDs

For unique resource-IDs we recommend enumerating them, as in

this example:

using Remotion.Globalization;
namespace MyProject {
  class MyPage : System.Web.UI.Page {
    [ResourceIdentifiers]
    public enum ResourceIdentifiers {
      ID1,
      ID2
} } }

The enum can be part of a page or a user control or can be defined
as "stand-alone".

Accessing resource-IDs

The typical place where to put resources is into resource files identified
by a MultiLingualResources attribute. The demand outlined above for pages or
user controls accessing the resource applies to resource-IDs as well:
They shall be accessed via IResourceManager and ResourceManagerUtility.

using Remotion.Globalization;
using Remotion.Web.UI.Globalization;
IResourceManager rm = ResourceManagerUtility.GetResourceManager (this);

The resource entry itself is identified by the enum value:

string valueID1 = rm.GetString (ResourceIdentifiers.ID1);

In the resource file itself the entry is identified by

  • its namespace
  • a class name
  • an enum value
  • (e.g. MyProject.MyPage.ID1)

If an enum is not within a page, the following string is used as a
substitute: MyProject.ResourceIdentifiers.ID1. In other words,
the resource-ID is composed of

  • its namespace
  • the enum type
  • the enum value

You can derive a list of all resource-IDs defined with a ResourceIdentifiersAttribute
in a module with the resource summary utility. Xquestion

Defining global resources

If resources are required outside an ASP.NET page, you can use
the global ResourceManagerProvider. The ResourceManagerProvider
is a singleton where you can register your implementations of
IResourceManager or IObjectWithResources.

Registering global resources

The mechanics should be obvious from the following sample snippets.
For IObjectWithResources implementations:

using Remotion.Globalization;
ResourceManagerProvider.Register (IObjectWithResources objectWithResources);

or, for IResourceManager implementations:

using Remotion.Globalization;
ResourceManagerProvider.Register (Type type, IResourceManager resourceManager);

The ResourceManagerProvider caches the registered IResourceManager s
in a static hash-table, using the passed type parameter as key. Therefore
you should only register IResourceManager s that don't change between
post-backs. What's more, there should be no dependencies between those
IResourceManager objects, because hashing makes the order of evaluation
unpredictable. For ASP.NET we recommend to register global resources
through the HttpApplication instance:

using Remotion.Globalization;
[MultilingualResourcesAttribute ("MyApplication.Globalization.Global")]
public class Global : System.Web.HttpApplication, IObjectWithResources
{
  protected void Application_Start(Object sender, EventArgs e) {
    ResourceProvider.Register (this);
  }
  // IObjectWithResources Implementation
}
Accessing global resources

The ResourceManagerProvider gives you the registered IResourceManager
implementations as a combined IResourceManager object. Due to hashing,
their order is unpredictable. Here is a sample code snippet:

using Remotion.Globalization;
IResourceManager rm = ResourceManagerProvider.GetResources ();

The resource entry itself is identified by a string or an enum value,
just as with page- or control-local resources:

string valueRes1 = rm.GetString ("Res1");

or

string valueID1 = rm.GetString (ResourceIdentifiers.ID1);

By default, the ResourceManagerUtility includes the ResourceManagerProvider
in the returned IResourceManager instance. The global resources have the
lowest priority of those, i.e. resources of a page or control can override them.

Multilingual ASP.NET applications

ID-dependent resources

You can define resources for each control individually, for example
the Text property for a label. This requires the ResourceDispatcher
facility. You specify the resource-IDs for a uniquely identified control
on the page, for example:

auto:FirstNameLabel:Text

FIXME: Xquestion: "Für die Resource-IDs komplexer Properties sei auf die
Dokumentation des jeweiligen Controls verwiesen".

The best place where to dispatch a resource is in the pre-render phase
of the page. Sample code:

IResourceManager rm = ResourceManagerUtility.GetResourceManager (this);
ResourceDispatcher.Dispatch (this, rm);

Defining resources for properties of the page itself is also possible,
the page is simply called this, as in

auto:this:Title
Shared resources

You can assign a resource to multiple controls (e.g. a validator
message) by simply using its ID in the property's value. Examples:

FirstNameRequiredValidator.ErrorMessage = "$res:RequiredValidationError";
LastNameRequiredValidator.ErrorMessage = "$res:RequiredValidationError";

It is usually more convenient to set the property in Visual Studio's
designer; just use the resource-ID instead of the message itself.

In the resource file itself the prefix $res is redundant and
not allowed. For the example above, the ID is written as
RequiredValidationError in the resource file.

FIXME: xquestion: "Mehrfach verwendete Ressourcen werden dabei
vor den ID-abhängigen Ressourcen ausgewertet, und können somit
durch diese bei Bedarf überschrieben werden."

This feature is implemented by the controls themselves, not
the resource manager. However, the controls in Remotion.Web
and Remotion.ObjectBinding.Web do support this feature.

re:motion controls

The controls in the Remotion.Web.* and Remotion.ObjectBinding.Web
assemblies come with standart text for English and German. For those
languages you need to localize only the field identifiers of your application.

A good place for localizing strings for other languages is in the
resources for the BasePage. A better place is in the globally defined
resources. The IDs of the desired resources can be determined with the
resource summary utility. Xquestion.

Selecting a language

For enabling the correct language for resources you must set
the thread's CurrentUICulture. CurrentCulture is only used for
formatting (dates, numbers, etc.) The culture should be set during
the load phase (or earlier), otherwise complete coverage of all affected
stages by the correct language cannot be assured. Sample code:

using System.Threading;
Thread.CurrentThread.CurrentCulture = // CultureInfo Object
Thread.CurrentThread.CurrentUICulture = // CultureInfo Object

There are two ways to find out which language to select
for the user:

  • Provide options in the application (hyperlinks, user settings).
  • Inspect the UserLanguages collection of the Page.Request
    object. It holds the language selected in the user's browser.

If you don't set the culture in your code, the framework
inspects the globalization section in the Web.config file.
If no entry is found, the setting in the process is used as
the default culture.

  • No labels