In WPF la localizzazione è ‘ufficialmente’ compito di LocBaml, tool che personalmente preferisco evitare per una lunga serie di motivi, non ultima il fatto che la non è applicabile a Silverlight.
Per localizzare le applicazioni basate sul pattern Model-View-ViewModel la soluzione che utilizzo è la seguente.

Aggiungo al mio progetto i vari files .resX che contengono i vari testi localizzati:

image image image
image

Successivamente da ViewModelBase:

   1: public class ViewModelBase : INotifyPropertyChanged
   2:    {
   3:       public event PropertyChangedEventHandler PropertyChanged;
   4:  
   5:       protected void OnPropertyChanged(string propertyName)
   6:       {
   7:          if (PropertyChanged != null)
   8:          {
   9:             PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
  10:          }
  11:       }
  12:    }

Eredito un generico LocalizableViewModel che rappresenta il ViewModel base per tutte quelle View che devono essere localizzate:

   1: public abstract class LocalizableViewModel:ViewModelBase
   2:    {
   3:       private static Strings resources = new Strings();
   4:       
   5:       public LocalizableViewModel():base()  { }     
   6:  
   7:       protected void SetCulture(string userCulture)
   8:       {
   9:          if (Thread.CurrentThread.CurrentUICulture.Name != userCulture)
  10:          {            
  11:             Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(userCulture);
  12:             this.OnPropertyChanged("LocalizedText");
  13:          }
  14:       }
  15:  
  16:       public Strings LocalizedText
  17:       {
  18:          get
  19:          {
  20:             return resources;
  21:          }
  22:       }   
  23:    }

A questo punto un generico ViewModel non deve far altro che ereditare da LocalizableViewModel:

   1: public class ShellViewModel:LocalizableViewModel
   2: {
   3:    private List<string> availableCultures = new List<string>() { "it-IT", "en-US", "fr-FR" };
   4:    private string selectedCulture;
   5:  
   6:    public ShellViewModel():base(){}
   7:  
   8:    public List<string> AvailableCultures
   9:    {
  10:       get { return this.availableCultures; }
  11:    } 
  12:  
  13:    public string SelectedCulture
  14:    {
  15:       get
  16:       {
  17:          return this.selectedCulture;
  18:       }
  19:  
  20:       set
  21:       {
  22:          if (value != this.selectedCulture)
  23:          {
  24:             this.selectedCulture = value;
  25:             this.SetCulture(value);
  26:             this.OnPropertyChanged("SelectedCulture");
  27:          }
  28:       }
  29:    }
  30: }

Ora associamo il ViewModel alla view utilizzando la modalità che preferiamo:

   1: <Window
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     xmlns:local="clr-namespace:D08_LocalizationOnMVVM"
   5:     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" x:Class="D08_LocalizationOnMVVM.Window1"
   6:     Title="Window1" Height="300" Width="300" mc:Ignorable="d">
   7:    <Window.DataContext>
   8:       <local:ShellViewModel />
   9:    </Window.DataContext>
  10:    <Grid>
  11:       <TextBlock Height="20"
  12:                  Margin="92,0,84,182"
  13:                  x:Name="textBlock1"
  14:                  VerticalAlignment="Bottom"
  15:                  Text="{Binding LocalizedText.WelcomeText, Mode=OneWay}"
  16:                  FontSize="18" />
  17:       <Button Margin="82,117,75,122"
  18:               x:Name="button1"
  19:               Content="{Binding LocalizedText.ButtonContent, Mode=OneWay}" />
  20:       <ComboBox x:Name="CultureComboBox"
  21:                 ItemsSource="{Binding AvailableCultures}"
  22:                 Height="25"
  23:                 SelectedItem="{Binding SelectedCulture, Mode=TwoWay}"
  24:                 SelectedIndex="0"
  25:                 Margin="82,0,75,75"
  26:                 VerticalAlignment="Bottom">        
  27:       </ComboBox>
  28:    </Grid>
  29: </Window>

Ottenendo una View che può così cambiare la propria lingua in tempo reale:

image image  image

Il procedimento funziona sia con WPF che Silverlight, in quest’ultimo caso bisogna solo ricordarsi di editare il file di progetto e inserire manualmente le culture supportate nel tag <SupportedCultures>