Usare WPF Localization Extension con un ContextMenu definito nelle risorse di una Window
Mi è stato chiesto di sviluppare uno UserControl WPF multilingua, quindi in grado di switchare sia a design-time che a run-time da una lingua all’altra. Ovviamente mi sono buttato subito sull’extension WPF Localization Extension. Direi che è semplice da utilizzare (una volta capiti bene i suoi meccanismi) e fa quello che promette. Supporta il passaggio da una lingua all’altra a run-time, supporta una lingua da visualizzare a design-time, ed è facile aggiungere nuove lingue, dal momento che è sufficiente creare un nuovo file di risorse dedicato alla nuova lingua.
Do per scontato che sappiate già utilizzare questa extension. Supponiamo di avere una Window definita come segue:
<Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:lex="http://wpflocalizeextension.codeplex.com" lex:LocalizeDictionary.DesignCulture="en-US" lex:ResxLocalizationProvider.DefaultAssembly="WpfApplication1" lex:ResxLocalizationProvider.DefaultDictionary="Strings" Title="MainWindow" Height="350" Width="525"> <Grid> <Button Content="{lex:Loc Ok}" Width="100" Height="30" /> </Grid> </Window>
Qui si instanzia il WPF Localization Extension, dicendo che la culture a design-time è “en-US”, che le risorse sono contenute nell’assembly “WpfApplication1” e che i file di risorse si chiamano “Strings.resx” (e di conseguenza Strings.it-IT.resx, Strings.fr-FR.resx, e via dicendo).
Il problema nasce quando si vuole definire un ContextMenu nelle risorse della Window. D’istinto verrebbe da scrivere il seguente codice XAML:
<Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:lex="http://wpflocalizeextension.codeplex.com" lex:LocalizeDictionary.DesignCulture="en-US" lex:ResxLocalizationProvider.DefaultAssembly="WpfApplication1" lex:ResxLocalizationProvider.DefaultDictionary="Strings" Title="MainWindow" Height="350" Width="525"> <Window.Resources> <ContextMenu x:Key="m"> <MenuItem Header="{lex:Loc Menu1}" /> <MenuItem Header="{lex:Loc Menu2}" /> <MenuItem Header="{lex:Loc Menu3}" /> </ContextMenu> </Window.Resources> <Grid> <Button Content="{lex:Loc Ok}" ContextMenu="{StaticResource m}" Width="100" Height="30" /> </Grid> </Window>
In pratica, abbiamo aggiunto un ContextMenu con x:Key=”m”, e lo si utilizza all’interno del Button. Peccato che il WPF Localization Extension non sia in grado di trovare le stringhe localizzate. Ve ne accorgete perchè invece di vedere la stringa prelevata dal vostro file di risorsa, trovate la stringa “Key:Menu1” (vado a memoria), segno inequivocabile che qualcosa è andato storto. E’ vero quello che ho appena scritto? Non proprio. Va sempre bene utilizzare la markup extension {lex:Loc}, ma va utilizzata una sintassi differente, che è la seguente:
<ContextMenu x:Key="m"> <MenuItem Header="{lex:Loc WpfApplication1:Strings:Menu1}" /> <MenuItem Header="{lex:Loc WpfApplication1:Strings:Menu2}" /> <MenuItem Header="{lex:Loc WpfApplication1:Strings:Menu3}" /> <MenuItem Header="{lex:Loc WpfApplication1:Strings:Menu4}" /> <MenuItem Header="{lex:Loc WpfApplication1:Strings:Menu5}" /> </ContextMenu>
Non ho trovato una documentazione ufficiale (ma io con i motori di ricerca, si sa, non sono bravissimo), ma direi che la sintassi sia: <namespace>:<file_di_risorsa:<chiave>. Strana sintassi, perchè bisogna separare ogni parte con un “:”, come si vede dallo stralcio di XAML qui sopra.
Et voilà, il gioco è fatto! Così il ContextMenu viene correttamente popolato con le traduzioni prese dal file di risorsa, traduzioni che cambiano anche a run-time nel caso in cui switchate da una lingua all’altra programmaticamente agendo sull’oggetto singleton LocalizeDictionary.