I’m finally to the point where I can start using my localization provider code to actually localize a few applications and pages <s>. Took a while to get everything working correctly and get the live resource editing hooked up and little kinks worked out.
So the first task is to actually localize (or rather make localization ready) the various admin forms and helpers for the Localization provider.
The first thing I ran into is that by far the hardest part of the localization process is not the UI control assignments, but any text assignments in code. Yech. This is made even worse by the fact that the entire admin interface is driven mostly through AJAX code so a lot of strings are embedded on the client side in script code.
So, ASP.NET Pages make it pretty easy to get localized ‘objects’ back using Control.GetLocalResourceObject() and Control.GetGlobalResourceObject(). These two functions allow you to get a provider specific resource without having to muck with resource managers. Behind the scenes ASP.NET has hooked up the appropriate ResourceProvider and is asking it to return to you the appropriate ResourceKey.
In code this is pretty straight forward. Anywhere in Page code you can do:
string Value = this.GetGlobalResourceObject("westwindglobalization","NoReourcesFound") as string;
You can also get this stuff into the markup using classic ASP style expressions:
<%= GetGlobalResourceObject("westwindglobalization","NoReourcesFound") %>
I know some folks shutter at using <%= %> expressions, but if you have literal text in certain places you can’t easily get around using these expressions. For example in script code where it gets kinda ugly:
<script type="text/javascript">
alert('<%= GetGlobalResourceObject("westwindglobalization","Loading") %>');
…
Or for the more politically correct:
<script type="text/javascript">
alert('<asp:Localize runat="server">My Text</asp:Localize>');
The advantage of the latter is that you can assign a meta:resourcekey to the ASP.NET 2.0 Localize control:
alert('<asp:Localize runat="server" id="locSomeText" meta:resourcekey="LoaderString">Some Text</asp:Localize>');
Well, it’s supposed to work, but unfortunately I can’t get the resource key to work either inside of script or just in the middle of the page proper. VS.NET generated a Resource key but for some reason the Implicit key is never fired at runtime <shrug>.
In any case typically I prefer to have explicit resource strings stored in global resources where they can hopefully be reused more frequently.
But there’s another problem with the syntax of both entries above: Both will fail if the string returned includes a single quote as the output is generated as a literal string as well as any escaped characters that can potentially be misinterpreted by the client script parser if the literal is placed inside of JavaScript code.
Sooo… to make things a little easier in client code (both markup and script) I think it’s probably a good idea to create a few helper functions with shorter names and simpler syntax.
#region Localization Helper Functions
/// <summary>
/// Global Resource Help Function for easier syntax
/// </summary>
/// <param name="ResourceKey"></param>
/// <param name="ResourceSet"></param>
/// <returns></returns>
public string Res(string ResourceKey, string ResourceSet)
{
string Value = this.GetGlobalResourceObject(ResourceSet, ResourceKey) as string;
if (Value == null)
return ResourceKey;
return Value;
}
/// <summary>
/// Global Resource Helper function for easier access.
/// Defaults resource set and requires only the ResourceId
/// </summary>
/// <param name="ResourceKey"></param>
/// <returns></returns>
public string Res(string ResourceKey)
{
return Res(ResourceKey, "westwindglobalization");
}
/// <summary>
/// Creates a client side compatible string including the quote characters.
/// This simplifies adding values to client script with code like this:
///
/// <%= ResC("ResourceID") %>
/// </summary>
/// <param name="ResourceKey"></param>
/// <returns></returns>
public string ResC(string ResourceKey)
{
string Value = Res(ResourceKey, "westwindglobalization");
return EncodeJsString(Value);
}
/// <summary>
/// Encodes a string to be represented as a string literal
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
string EncodeJsString(string s)
{
StringBuilder sb = new StringBuilder();
sb.Append("\"");
foreach (char c in s)
{
switch (c)
{
case '\"':
sb.Append("\\\"");
break;
case '\\':
sb.Append("\\\\");
break;
case '\b':
sb.Append("\\b");
break;
case '\f':
sb.Append("\\f");
break;
case '\n':
sb.Append("\\n");
break;
case '\r':
sb.Append("\\r");
break;
case '\t':
sb.Append("\\t");
break;
default:
int i = (int)c;
if (i < 32 || i > 127)
{
sb.AppendFormat("\\u{0:X04}", i);
}
else
{
sb.Append(c);
}
break;
}
}
sb.Append("\"");
return sb.ToString();
}
#endregion
With string expressions in script code look like this:
function GetResourceStrings_Callback(Resources)
{
// returns a two col array
if (Resources == null)
{
this.ShowMessage(<%= ResC("NoResourcesAvailable") %>);
return;
}
…
ResC adds outer quotes and escapes any quotes inside of the string JSON style.
For any regular page expressions:
<%= Res("NoResourcesAvailable") %>
Or if databinding is involved:
<%# Res("NoResourcesAvailable") %>
This makes life somewhat easier. The easiest approach however would be if strongly typed resources worked properly in ASP.NET, but I don’t have time at the moment to build that out.
I suppose there could be other ways to tackle this such as pre-generating all the strings into an object on the client and then using the object (like Res.NoResourcesAvailable) to get the resource. But I honestly don't see any big advantages to that except that all literals are all in one place. You still have to assign them into code somehow and ensure that the names match properly. All of this is brittle if there's a typo - the code above deals with this by returning the ResourceId as the result value if the ResourceId was not found.
Other Posts you might also like