When tasking for assembly resources, especially for localized strings, I prefer putting them in a separate assembly.
Main problem I faced in that approach is to make the resources visible outside the assembly, export them. As you know, the generated classes by ResXFileCodeGenerator for strong type resource are not public. They are not even partial, which can give Us the ability to solve the issue.
The common solution is the add some public utility class to the work. Those classes as usual will have some methods to return the resource by provided key.
The main disadvantages of that approach are:
- losing the strong type view of the resource;
- cannot bind the resources in the markup, but have to set all the localized strings in the code behind;
I like the way to bind the resource in the markup through the existing Resources expression builder.
For example:
<asp:Label runat="server" Tex="<%$ Resources: Strings, UserName %>"></asp:Label>
The problem with the above example is that works for the internal assembly resources.
So, I needed a way to put my localization resources in a separated assembly and still be able to bind them in the markup like using the Resources expression builder.
As I mentioned above, Resources is an expression builder. Then looks natural to me to create an expression builder to solve my problem.
I have created the abstract class ExportResourceExpressionBuilder as a base implementation of ExpressionBuilder and abstract property to plug the resource manager later.
Here is the class definition:
[ExpressionEditor(typeof(ExportResourceExpressionEditor))]
public abstract class ExportResourceExpressionBuilder : ExpressionBuilder {
#region Properties ///////////////////////////////////////////////////////////////////////
/// <summary>
/// Gets the resource manager.
/// </summary>
/// <value>The resource manager.</value>
public abstract ResourceManager ResourceManager {
get;
}
#endregion
#region Methods ///////////////////////////////////////////////////////////////////////////
/// <summary>
/// When overridden in a derived class, returns an object that represents the parsed expression.
/// </summary>
/// <param name="expression">The value of the declarative expression.</param>
/// <param name="propertyType">The type of the property bound to by the expression.</param>
/// <param name="context">Contextual information for the evaluation of the expression.</param>
/// <returns>
/// An <see cref="T:System.Object"/> containing the parsed representation of the expression; otherwise, null if <see cref="M:System.Web.Compilation.ExpressionBuilder.ParseExpression(System.String,System.Type,System.Web.Compilation.ExpressionBuilderContext)"/> is not implemented.
/// </returns>
public override object ParseExpression(string expression, Type propertyType, ExpressionBuilderContext context) {
return expression;
}
/// <summary>
/// When overridden in a derived class, returns code that is used during page execution to obtain the evaluated expression.
/// </summary>
/// <param name="entry">The object that represents information about the property bound to by the expression.</param>
/// <param name="parsedData">The object containing parsed data as returned by <see cref="M:System.Web.Compilation.ExpressionBuilder.ParseExpression(System.String,System.Type,System.Web.Compilation.ExpressionBuilderContext)"/>.</param>
/// <param name="context">Contextual information for the evaluation of the expression.</param>
/// <returns>
/// A <see cref="T:System.CodeDom.CodeExpression"/> that is used for property assignment.
/// </returns>
public override CodeExpression GetCodeExpression(BoundPropertyEntry entry, object parsedData, ExpressionBuilderContext context) {
ResourceManager manager = this.ResourceManager;
if (manager != null) {
string name = Convert.ToString(parsedData);
if (!string.IsNullOrEmpty(name)) {
return new CodePrimitiveExpression(this.ResourceManager.GetString(name));
}
}
return new CodePrimitiveExpression("");
}
#endregion
}
Then in assembly of my localization resources I'm adding a simple implementation of ExportResourceExpressionBuilder, where I just have to plug the ResourceManager.
Let's say I have my resources in Strings.resx and the generated class will be Strings. In that case I will have next implementation for example:
public class StringsExpressionBuilder : ExternalResourceExpressionBuilder {
#region Properties ///////////////////////////////////////////////////////////////////////
/// <summary>
/// Gets the resource manager.
/// </summary>
/// <value>The resource manager.</value>
public override System.Resources.ResourceManager ResourceManager {
get { return Strings.ResourceManager; }
}
#endregion
}
Now, I'm ready to use my exported localized resources.
For that I need:
- to add a reference to the resources assembly to my web project.
- configure my expression builder in Web.config;
- use it in the markup where I need it;
More details can be found under my toolbox here.
You can get the source and built assembly from here.
Check out the article sample video here.
Happy coding ...
Posted
03-06-2009 21:26
by
velio