Localizzare una form o altro

Premessa
Localizzare un'applicazione, cioè renderla fruibile a utenti di culture diverse, non è soltanto questione di tradurre qualche stringa da una lingua all'altra, ma coinvolge pure formati di visualizzazione (che possono essere diversi per la data o per i numeri o per la valuta, vedi Impostazioni Internazionali nel Pannello di controllo).

Sembra un compito alquanto cervellotico, stando a quanto si trova in giro. Sembra tutto eccessivamente complicato.
Con questo post mi propongo di far vedere come invece sia semplice, benché i passi da fare siano diversi.

Creazione del progetto di studio
Avvio Visual Basic 2008 Express e creo un nuovo progetto di tipo Windows Forms Application, di nome LocalizedApplication.

La prima cosa che faccio, dato che sto specificamente studiando questo argomento, è impostare a True la proprietà Localizable della Form1.

Ma 'funziona' anche se lo si fa in un secondo momento. Quando si imposta a True questa proprietà, Visual Basic Express 'lavora' a cambiare il codice di inizializzazione dei componenti già aggiunti alla form. Farlo subito significa solo che questo lavoro viene fatto man mano che i controlli vengono aggiunti.

Aggiungo tre RadioButton alla Form, impostando a True la proprietà Checked del primo.

Cambio i nomi dei RadioButton in DefaultRadioButton, EnglishRadioButton e ItalianRadioButton, rispettivamente.

Questa è la situazione di default, adesso comincio a localizzare: imposto la proprietà Language della Form1 a English:

Adesso, l'IDE mi informa che sono in progettazione di form localizzata:

Cambio la proprietà Text della Form1 e dei RadioButton:

Così vedo che Visual Basic Express ha prodotto il nuovo file di risorse:

Seguo la stessa procedura per localizzare in italiano:

Adesso implemento la gestione del click sui RadioButton. Per fare ciò devo tornare nella progettazione 'di default', non localizzata, quindi riporto la proprietà Language della Form1 su default. Produco un gestore per l'evento clic di uno di essi e poi ne cambio la firma in modo da gestire tutti e tre gli eventi:

  Private Sub RadioButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
                                Handles DefaultRadioButton.Click, EnglishRadioButton.Click, _
                                ItalianRadioButton.Click

Per ottenere la lingua cui si riferisce ciascun RadioButton, decido di usarne la proprietà Tag. Valorizzo quindi EnglishRadioButton.Tag con 'en' e ItalianRadioButton.Tag con 'it'.

Esaminando il codice prodotto in Form1.Designer.vb da Visual Basic Express per l'inizializzazione della Form1, scopro due righe interessanti:

    Dim resources As System.ComponentModel.ComponentResourceManager = _
                 New System.ComponentModel.ComponentResourceManager(GetType(Form1))

    resources.ApplyResources(Me.DefaultRadioButton, "DefaultRadioButton")

Quindi mi accingo a copiarle. Non è molto probabile che questa operazione di cambio di lingua venga fatta spesso, quindi giudico che non vale la pena di istanziare il ComponentResourceManager a livello di dichiarazioni della Form1.
Ma se le Form fossero ben più di una, sarebbe il caso di creare un modulo a parte con un metodo accessibile da tutte le form dell'applicazione. Qui mi limito alla sola Form1.

  Private Sub RadioButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
                                Handles DefaultRadioButton.Click, EnglishRadioButton.Click, _
                                ItalianRadioButton.Click

    'dichiaro e istanzio il ComponentResourceManager specifico di questa form
    Dim MyRm As New System.ComponentModel.ComponentResourceManager(GetType(Form1))

    ' la form 'nasce' con la UICulture del sistema del pc dell'utente 
    Static defaultCulture As String = My.Application.UICulture.Name

    ' ottengo la sigla della lingua scelta, posta nel Tag del RadioButton
    Dim cultureTag As Object = DirectCast(sender, RadioButton).Tag

    ' a seconda del suo valore, imposto la nuova cultura di interfaccia
    If cultureTag Is Nothing Then
      My.Application.ChangeUICulture(defaultCulture)
    Else
      My.Application.ChangeUICulture(cultureTag.ToString)
    End If

    'quindi applico la modifica alla form e a tutti i suoi controlli
    MyRm.ApplyResources(Me, "$this")
    For Each ctl As Control In Me.Controls
      MyRm.ApplyResources(ctl, ctl.Name)
    Next

  End Sub

Senza il namespace My, avrei dovuto usare le seguenti istruzioni per impostare la nuova cultura di interfaccia (mostro solo le righe essenziali):

    Dim ci As Globalization.CultureInfo
    ci = New Globalization.CultureInfo(defaultCulture)
    Threading.Thread.CurrentThread.CurrentUICulture = ci

Adesso posso eseguire il programma per verificare che tutto funziona come mi aspetto.

Risorse non legate a controlli ma alla stessa form
Poniamo il caso che ai compiti svolti da questa Form1 (e solo da questa) sia legata l'eventualità di dover mostrare un messaggio. Mi sembra naturale aggiungere una risorsa a ciascun file di risorse. Comincio da quello di default, faccio doppio clic su Form1.resx e Visual Basic Express mi presenta l'editor di risorse. Nell'ultima riga voglio scrivere nome e valore per la nuova risorsa, ma mi viene presentato questo avviso:

E' un avviso che ricorda al programmatore che le modifiche fatte a mano devono essere congruenti con il resto (cioè, a esempio, con gli altri file di risorse), a scanso di corruzioni del progetto che Visual Basic Express non può correggere.
Io mi fido di me stesso, quindi confermo la mia intenzione e aggiungo la nuova risorsa (mostro le ultime quattro righe e la barra con l'impostazione del tipo di risorsa esposto):

Adesso seleziono la riga appena immessa e la copio. Quindi apro gli altri due file di risorse e la incollo sull'ultima riga di ciascuno, modificando poi adeguatamente il valore.

Nel punto in cui devo mostrare il messaggio - cioè nella stessa procedura di evento di cui sopra - devo istanziare un ResourceManager sulla base della stessa form (non più un semplice ComponentResourcemanager) e ricavare la risorsa appena aggiunta usando uno degli specifici metodi (GetString, GetObject, eccetera) passando a esso il nome della risorsa (come espressione costante, il che è non solo una scocciatura, ma anche una debolezza del sistema).
Questo è il codice aggiunto in fondo alla Sub:

    Dim rm As New System.Resources.ResourceManager(GetType(Form1))
    MessageBox.Show(rm.GetString("Messaggio"))

Funziona (a parte qualche imperfezione che non mi interessa correggere in questa fase), ma non mi piace.

A questo punto salvo tutto (soluzione e progetto).

Risorse non legate a form ma relative a tutta l'applicazione
Come si gestisce la localizzazione delle risorse relative a tutta l'applicazione? Visual Basic Express mi fornisce un solo Resources.resx e apparentemente non c'è modo di farne un altro in modo il più possibile automatico. Ma naturalmente c'è.

Prima di tutto inserisco delle risorse nel file risorse di default, Resources.resx, dopo averlo aperto nell'editor di risorse (come ho fatto con gli altri resx):

Adesso preparo la localizzazione:

  1. Facendo clic col pulsante destro del mouse sul nome del progetto, aggiungo una sottocartella di nome Risorse.
  2. Seleziono Resource.resx e lo copio
  3. Seleziono la cartella Risorse e incollo
  4. Rinomino il file interendo la sigla della lingua (en o it)
  5. Faccio la stessa cosa per l'altra lingua

Non mi resta quindi che aprire ciascun file e modificare adeguatamente i valori delle risorse:

Notate che ho inserito non solo una risorsa 'Messaggio' relativa a tutta l'applicazione, ma anche una risorsa 'Form1Messaggio' in cui ho ripreso il relativo valore della risorsa Messaggio dai file di risorse relativi alla Form1.
Infatti mi scoccia dovermi ricordare del nome della risorsa e doverlo digitare ogni volta che ne ho bisogno.
Con il 'trucco' di denominare la risorsa facendola precedere dal nome dell'oggetto di riferimento, posso approfittare della notevole comodità di avere i nomi delle risorse come membri a tutti gli effetti del mio gestore di risorse. Infatti, posso sostituire le ultime due righe della procedura con:

    MessageBox.Show(My.Resources.Form1Messaggio)

E quando digito il punto dopo Resources, Intellisense mi presenta i membri del gestore di risorse:

Concludo aggiungendo il titolo alla MessageBox, giusto per togliermi lo sfizio di vedere anche i valori della risorsa Messaggio:

    MessageBox.Show(My.Resources.Form1Messaggio, My.Resources.Messaggio)

Conclusione
Con Visual Basic 2008 Express è facile.

Print | posted @ martedì 5 maggio 2009 22:07

Comments on this entry:

Gravatar # re: Localizzare una form o altro
by Gerardo at 11/02/2012 17:48

Ciao, complimenti per l'articolo
chiaro e sintetico.
Volevo chiederti; per vs2010 cambia qualcosa?
Grazie
Saluti
Gerardo
Comments have been closed on this topic.