Quando si sviluppa un applicativo multilingua, è facile muoversi con le funzionalità offerte dal framework, ad esempio diventa fattibile abbastanza facilmente cambiando la cultura del Thread corrente, e lavorando nel codice con i files di risorse.
Quindi l'approccio è abbastanza standard, e si parte con il creare un file di risorse:
dopodichè al momento della compilazione, Visual Studio genererà per noi la classe che "wrappa" quelle risorse in termini di chiave/valore, e da codice possiamo avere accesso al valore usando le sue proprietà statiche, sia nel codice di markup, sia nel codice vero e proprio, ad esempio:
LogManager.Write(Testi.Disconnessione);
e nel codice di markup:
<TextBlock Text="{x:Static lingue:Testi.Conferma}" Grid.Row="2" />
dove quel "lingue" è un mio namespace che referenzia il mio assembly con le traduzioni):
... xmlns:lingue="clr-namespace: ...
Come dicevamo possiamo cambiare la cultura del thread e il programma per noi si incarica di caricare in quella classe la traduzione nella lingua corretta:
public static void Imposta(string lingua)
{
//---- safety code
if (string.IsNullOrEmpty(lingua))
return;
string cultura = "it-IT";
switch (lingua.ToLower())
{
case "english":
case "en-us":
cultura = "en-US";
break;
case "français":
cultura = "fr-FR";
break;
case "portugues":
cultura = "pt-PT";
break;
case "espanol":
cultura = "es-ES";
break;
case "deutsch":
cultura = "de-DE";
break;
}
try
{
CultureInfo cult = new CultureInfo(cultura);
if (Thread.CurrentThread.CurrentUICulture.Name != cult.Name)
{
Thread.CurrentThread.CurrentCulture = cult;
Thread.CurrentThread.CurrentUICulture = cult;
}
}
catch { }
}
}
Oltre a questo, ci sarebbe tutto il discorso della Localizable della Form, quindi anche a livello di proprietà dei controlli etc...ma al momento non è importante per questo post.
Quello che volevo invece analizzare in questo post è l'applicazione di queste tecniche ad un Report RDLC, infatti se per l'applicazione è possibile tradurre nativamente tramite il motore del framework, il report non ha qualcosa di già fatto.
Di fatto però il file di report RDLC non è altro che un file XML, quindi è possibile andare a tradurre i valore di alcuni nodi, in base a qualche segnaposto inserito durante il design del report, in questo caso ValueLocID:
E quindi è possibile andare a tradurre il report, andando a cercare appunto quel ValueLocID, nell'esempio "Proprietario" è la chiave, e poi viene tradotta con un codice di questo genere:
public static Stream TranslateReport(string xml)
{
string ns1 = @http://schemas.microsoft.com/sqlserver/reporting/2008/01/reportdefinition;
string ns2 = @http://schemas.microsoft.com/SQLServer/reporting/reportdesigner;
XDocument reportXml = XDocument.Parse(xml);
foreach (var element in reportXml.Descendants(XName.Get("Value", ns1)))
{
XAttribute attribute = element.Attribute(XName.Get("LocID", ns2));
if (attribute != null)
{
string translatedValue = TraduzioniManager.Traduci(attribute.Value);
element.Value = string.IsNullOrEmpty(translatedValue) ? element.Value : translatedValue;
}
}
Stream ms = new MemoryStream();
reportXml.Save(ms, SaveOptions.OmitDuplicateNamespaces);
ms.Position = 0;
return ms;
}
dove il "Traduci" non fa altro che chiamare il metodo "GetString" del ResourceManager relativo a quella classe "Testi":
static ResourceManager manTesti = new ResourceManager(typeof(Testi));
public static string Traduci(string chiave)
{
return manTesti.GetString("...");
}
In questo modo, è possibile tradurre un report lavorando con l'attributo ValueLocID e un po' di codice del Framework, ma il motore è abbastanza generico anche per report diversi ed eventuali sottoreport.