Hi Habradrug!
Of course, I understand that the topic is already jaded, but I will try to describe my vision of the localization of the application and its implementation. And so, what we want to get in the end: The system of fast localization of .NET applications, and in particular WebForms, is at the post development stage. The points:
1. Application development: minimum attention localization.
2. Completion of development: a minimum of time for localization.
And it is also necessary that it would all be as convenient and simple as possible.
In short, everything will look like this:
')
We will have a method, something like a
gettext , to which we will transmit some text, or a key + text, and the method in turn will return a localized string or what was passed to it. And there will also be a utility that will parse the
* .cs ,
* .aspx ,
* .ascx files in search of this very method, get the keys and text from there, look, or the given texts and keys are already in the application resources. Then we will use google translate, so that at least poorly, but translate new texts. And finally, edit the translations and save it all back to the resources. Is done.
It is obvious that a similar, or maybe even the best mechanism has already been developed, but, having climbed the Internet, I saw only mega hard and, besides, paid solutions.
So, we will start implementation. Let's start with an auxiliary class, a for server-side controls with ExpressionBuilder-a:
public static class L
{
private static readonly Regex KeyRepairRegex = new Regex( "[^\\w\\d]" , RegexOptions.Compiled);
public static string Run( string value )
{
var key = GenerateKey( value );
var localized = Resources.ResourceManager.GetString(key);
return string .IsNullOrEmpty(localized) ? value : localized;
}
public static string Run( string key, string value )
{
var localized = Resources.ResourceManager.GetString(key);
return string .IsNullOrEmpty(localized) ? value : localized;
}
public static string Run( string key, string value , params object [] format)
{
var localized = Run(key, value );
if (format != null && format.Length > 0)
{
try
{
localized = string .Format(localized, format);
}
catch
{
}
}
return localized;
}
private static string GenerateKey( string value )
{
if ( string .IsNullOrEmpty( value )) return value ;
value = value .Length > 23 ? value .Substring(0, 23) + "__" : value ;
value = KeyRepairRegex.Replace( value , "_" );
if ( char .IsUpper( value [0])) value = "_" + value ;
value = value .ToLowerInvariant();
return value ;
}
}
Now, during development, in places where we need localization, we write:
<%= L.Run( "Hello, Mr.Everybody" ) %>
Class L is not specifically located in any namespace , so that you do not have to import it in * .ascx files.
<%@ Import Namespace = "Example.Namespace" %>
There is, as can be seen from the code, not a few important method GenerateKey . If we transmit only text, without a key, then this method will generate a key and it is this key that we will look for in resources. The key in resources can contain only the characters [a-z0-9_]. And our utility will use the same method to generate keys. Now we will consider ExpressionBuilder and give a more specific example of the whole scheme. Why do we need this very Builder?
The <% =%> construction in server controls will lead to an error, and for <% #%> you need to additionally call the DataBind method, which is not very convenient. We need a class inherited from ExpressionBuilder.
public class LExpressionBuilder: ExpressionBuilder
{
public override CodeExpression GetCodeExpression(BoundPropertyEntry entry, object parsedData, ExpressionBuilderContext context)
{
var thisType = new CodeTypeReferenceExpression(GetType());
var expression = new CodePrimitiveExpression(entry.Expression.Trim());
const string evaluationMethod = "Localize" ;
return new CodeMethodInvokeExpression(thisType, evaluationMethod, new CodeExpression[] { expression });
}
public static string Localize( string expression)
{
return L.Run(expression);
}
public override object EvaluateExpression( object target, BoundPropertyEntry entry, object parsedData, ExpressionBuilderContext context)
{
return Localize(entry.Expression);
}
public override bool SupportsEvaluate
{
get { return true ; }
}
}
As a result, we write:
<input typ= 'submit' id= 'btnSubmit' runat= 'server' value = "<%$ L:A people free to choose will always choose peace %>" />
The first part is ready. Go to the utility. This is an ordinary WinForms application.
Classes Chart
As can be seen from the diagram, the code is fairly straightforward. But the main thing is simple. We load the Solution or project we need, specify the path to our resource file in the Settings class. And ... "let's go"! Solution class will load all files. CodeParser will load all the strings and keys into the Translation class, which in turn will load all the translations from the resources and show it all in the GridView. Now you can translate manually or through google, you can save the original lines to a text file or something else to implement, for example, saving to xml .
Assembla Subversion (Registration is not required)
This utility was written for myself quickly, according to the principle: “if only it worked,” so I will ask the guru not to judge strictly. If someone has an interest, I can bring this business to, at least, the alpha version.
What else do you want to do:
CodeParser ripping out lines from files remembers their positions. And since most of the localized strings go to me initially without keys, several extra seconds are needed to generate them, so the utility should insert the keys to the strings into the files afterwards.
But even without this, everything works very fast, although I will be glad to hear any criticism or suggestions.
Thanks for attention.
* Source code was highlighted with Source Code Highlighter .