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 thePage.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.