La nuova versione di resgen contenuta nel framework 2.0 introduce un interessante flag: /str:(language) per cui scrivendo:

resgen /str:C# MyResFile.resx

non solo si ottiene il solito file .resources da passare al linker al.exe, ma anche un file .cs (o .vb) il quale contiene una classe che offre una visione tipizzata delle risorse contenute nel file MyResFile.resx.

Grazie a questa nuova feature in Visual Studio 2005 è stata introdotta una gestione delle risorse di applicazione che nulla ha a che vedere con quanto disponibile attualmente in Visual Studio 2003.
Selezionando la tab Resources nelle proprietà di un progetto VS 2005, appare un ottimo editor grazie al quale possiamo aggiungere tutte le risorse che andremo ad utilizzare siano essere stringhe, immagini,icone, wav files o semplicemente files che vogliamo "embeddare" nell'eseguibile.


Il risultato di questa operazione è la generazione, via resgen della classe tipizzata descritta in precedenza e quindi la possibilità di utilizzare tali risorse semplicemente scrivendo:


Se confrontiamo il tutto con Visual Studio 2003, oltre ad avere un editor di risorse evoluto (include anche un semplice editor di immagini) abbiamo la certezza di accedere alle risorse in maniera type-safe e quindi identificare eventali errori a compile time anzichè a run-time come avviene attualmente.
La classe generata si trova nel file resources.designer.cs (o vb se usate Visual Basic) ed è fondamentalmente è un wrapper attorno alla classe System.Resources.ResourceManager che espone i vari items come proprietà statiche della stessa.

internal class Resources
{
  private static global::System.Resources.ResourceManager
resourceMan;
  private static global::System.Globalization.CultureInfo
resourceCulture;

  internal static global::System.Resources.ResourceManager
ResourceManager
  {
  
get
 
 {
    if ((resourceMan == null
))
     {
      global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager  ("Localization_CS.Properties.Resources"
, typeof   (Resources).Assembly);
     resourceMan = temp;
     }
    return
resourceMan;
   }
  }

  internal static string Info {get {return ResourceManager.GetString("Info", resourceCulture);}
}

L'editor di risorse è collegato al motore di refactoring per cui modificando l'identificativo di una risorsa vengono automaticamente aggiornati tutti i riferimenti ad esso presenti nel codice.
I files .resX sono ora collegati ad un custom tool ResXFileCodeGenerator quindi la stessa gestione è applicabile a tutti i files .resX che aggiungeremo al nostro progetto.

Localizzazione dei applicazioni Windows con Visual Studio 2005

Il processo di localizzazione di una applicazione Windows sviluppata con Visual Studio 2005 è molto simile a quello di un applicazione creata con la versione 2003, ci sono però alcuni dettagli legati alla nuova implementazione che vale la pena descrivere.
Assumiamo di avere terminato la realizzazione della versione Inglese di MyApp, la quale ha un form con la proprietà Localizable=true e carica tutte le stringhe dalla classe Resources generata da Visual Studio e ha impostato nel file AssemblyInfo l'attributo

[assembly: System.Resources.NeutralResourcesLanguage("en-US")]

Ad indicare che l'assembly principale contiene le risorse Inglese-US e che questa è la cultura "Neutrale" della nostra applicazione, questo velocizza il processo di probing nel caso la lingua del S.O. coincida con quella della dichiarata con NeutralResourcesLanguage.


Il layout del progetto è quello in figura:

Volendo ora generare con Visual Studio la localizzazione per la lingua Spagnola parlata in Spagna (es-ES) senza toccare il codice originale, quello che dobbiamo fare è:

  • Utilizzare WinRes (presente nell SDK), caricare il file Form1.resx, localizzarne la UI e salvare il file selezionando la cultura Spagnola, questo produrrà un file Form1.es-ES.resx
  • Aggiungere alla nostra soluzione un nuovo progetto Class Library, il nome del progetto è ininfluente...
  • Allineare Assembly Name e Default (root) Namespace agli stessi valori impostati nel progetto principale.
  • Cancellare il file Class1.cs (o Class1.vb)
  • Aggiungere al progetto il file Form1.es-ES.resx generato da WinRes
  • Rinominare il file Resources.resx precedentemente creato da Visual Studio in Resources.es-ES.resx e aggiungere le stringhe localizzare oppure copiate il file originale, lo rinominate, e lo importate per velocizzare l'operazione.
  • Cancellate il file generato dal designer Resources.es-Es.designer.cs (è comunque vuoto...)
  • Nel file AssemblyInfo impostate opportunamente la cultura  dell'assembly a: [assembly: AssemblyCulture("es-ES")]
  • Compilate e copiate la cartella es-ES e il relativo contenuto nella cartella dove si trova MyApp.exe

Il layout dell'assembly satellite dovrebbe risultare all'incirca come riportato qui sotto:


Impostando la culture del sistema operativo a Spagnolo, oppure modificando da codice la UICulture del thread principale dell'applicazione vedrete la vostra applicazione localizzata in Spagnolo come pure localizzati saranno tutti i testi caricati a runtime.

Piccoli passi, ma che spesso fanno perdere pareccho tempo...

Ovviamente rimane sempre valido il concetto di "Fallback", ovvero se una determinata risorsa non è disponibile per una certa cultura, il framework inizia il processo di fallback fino ad utilizzare l'assembly contenente le risorse neutrali nel caso non esista una localizzazione che meglio si adatta a quella impostata nel thread corrente del nostro eseguibile.

Un ultimo accenno al versioning: Se l'eseguibile è strong named, anche le assemblies satellite localizzate devono esserlo.
Quando rilasciamo una nuova versione di MyApp però non vogliamo essere costretti a rilasciare parallelamente tutte le localizzazioni, anche perchè potrebbero non aver subito modifiche.
In questo caso andremo ad aggiungere all'assembly principale l'attributo:

[assembly: SatelliteContractVersion("1.0.0.0")]

Il quale informa il ResourceManager qual'è la versione di assembly satellite da caricare indipendentemente dalla versione dell' assembly principale.
Se a cambiare è invece l'assembly satellite, ad esempio a causa di un errata traduzione avete due possibilità:

  • Mantenere inalterata la verisone dell'Assembly e incrementare la sola AssemblyFileVersisn
  • Rilasciare insieme alla nuova assembly una policy che indica che la nuova versione è compatibile con quella precendente, in questo caso il ResourceManager continuerà a cercare la vecchia versione ma verrà reindirizzato dalla policy verso la nuova relese.
    Tutti i dettagli li trovate
    qui

Se con Visual Studio 2003 utilizzare le risorse significava necessariamente conoscere l'oggetto ResourceManager e sopratutto correre il rischio di avere dei run-time errors, la nuova gestione non solo è totalmente integrata nell'ambiente di sviluppo (basta pensare al refactoring automatico) ma rende l'uso delle risorse facile e, sopratutto, type-safe.