I have a Silverlight 4 OOB application which needs localizing. In the past I have used the conventional resx route but I have been asked to follow the architecture of an existing winforms app.
All the strings are currently stored in a database - I use a webservice to pull these down and write them into a local Effiproz Isolated Storage database. On Login I load a Dictionary object with the language strings for the users language. This works fine.
However, I want to automate the UI localization (the WinForms app does it like this): Loop through all the controls on the page and look for any Textblocks - if there is a text property I replace it with the localized version. If the text is not found, then I WRITE the string to the database for localization.
This works ok on simple forms but as soon as you have expanders/scrollviewers and content controls then the VisualTree parser does not return the children of the controls as they are not necessarily visible (see my code below). This is a known issue and thwarts my automation attempt.
My first question is: Is there a way of automating this on page load by looping through the complex (non-visual) elements and looking up the value in a dictionary?
My second question is: If not, then is the best way of handling this is to load the strings into an app resource dictionary and change all my pages to reference it, or should I look into generating resx files, either on the server (and package it with the app as per normal) or on the client (I have the downloaded strings, can I make and load resx files?)
Thanks for any pointers.
Here is my existing code that does not work on collapsed elements and complex content controls:
public void Translate(DependencyObject dependencyObject)
{
//this uses the VisualTreeHelper which only shows controls that are actually visible (so if they are in a collapsed expander they will not be returned). You need to call it OnLoaded to make sure all controls have been added
foreach (var child in dependencyObject.GetAllChildren(true))
{
TranslateTextBlock(child);
}
}
private void TranslateTextBlock(DependencyObject child)
{
var textBlock = child as TextBlock;
if (textBlock == null) return;
var value = (string)child.GetValue(TextBlock.TextProperty);
if (!string.IsNullOrEmpty(value))
{
var newValue = default(string);
if (!_languageMappings.TryGetValue(value, out newValue))
{
//write the value back to the collection so it can be marked for translation
_languageMappings.Add(value, string.Empty);
newValue = "Not Translated";
}
child.SetValue(TextBlock.TextProperty, newValue);
}
}
Then I have tried 2 different approaches:
1) Store the strings in a normal dictionary object 2) Store the strings in a normal dictionary object and add it to the Application as a Resource, then you can reference it as
TextBlock Text="{Binding Path=[Equipment], Source={StaticResource ResourceHandler}}"
App.GetApp.DictionaryStrings = new AmtDictionaryDAO().GetAmtDictionaryByLanguageID(App.GetApp.CurrentSession.DefaultLanguageId);
Application.Current.Resources.Add("ResourceHandler", App.GetApp.DictionaryStrings);