Quando sviluppiamo un'applicazione Windows 8, dobbiamo tener presente che grazie al Windows Store possiamo raggiungere un'audience diffusa in tutto il globo. Ovviamente, se decidiamo di sviluppare app esclusivamente in italiano, il numero dei potenziali utenti sarà decisamente minore.
Per fortuna, WinRT espone una serie di funzionalità che semplificano notevolmente la "globalizzazione" delle applicazioni. Il concetto di "globalizzazione" di un'app implica che questa può essere fruita in culture diverse; non deve essere confuso con la "localizzazione", che rappresenta invece il processo di personalizzazione di un'app per una lingua e/o una cultura specifiche.
La globalizzazione di un'app non riguarda unicamente la traduzione degli elementi testuali. A seconda della cultura, infatti, cambiano anche i modi di rappresentare le date, il calendario, i sistemi di misura, la valuta, ecc.
Perché sia possibile globalizzare un'app è necessario che nessuno degli elementi che dipendono dalla cultura e dalla lingua di un paese siano fissati una volta per tutte a livello di codice: piuttosto che visualizzare una data direttamente a schermo, ad esempio, questa dovrebbe essere formattata in base alle impostazioni correnti dell'utente.
In realtà, la globalizzazione di un'app richiede che venga prestata grande attenzione non solo alla lingua, alla formattazione di una data o alla scelta della giusta valuta, ma a molti altri elementi dell'app. Per facilitare il processo di internazionalizzazione dell'app, Microsoft ha messo a punto un ricco elenco di linee guida, la cui lettura è caldamente consigliata.
Le Globalization API
In Windows 8 è possibile impostare una o più lingue da usare, specificando anche l'ordine di preferenza. Per accedere alle impostazioni, è sufficiente aprire la Charm Bar, selezionare Change PC Settings
e quindi cliccare su Time and Language
per aprire il relativo pannello, mostrato nella prossima immagine.
È anche possibile impostare il formato della data e dell'ora, come mostrato nella prossima immagine.
Le preferenze impostate nel pannello Time and Language possono essere recuperate da codice grazie alle Globalization API, esposte dalla classe statica Windows.System.UserProfile.GlobalizationPreferences
. Questa classe espone le seguenti proprietà (in sola lettura):
Proprietà | Descrizione |
---|---|
Calendars | recupera i calendari selezionati dall'utente, nell'ordine di preferenza. |
Clocks | recupera gli orologi definiti dall'utente, nell'ordine di preferenza. |
Currencies | recupera le valute preferite dell'utente. |
HomeGeographicRegion | indica la regione geografica dell'utente. |
Languages | recupera le lingue selezionate dall'utente, nell'ordine di preferenza. |
WeekStartsOn | indica il primo giorno della settimana. |
Queste informazioni potranno essere poi utilizzate per decidere in quale lingua tradurre i testi dell'applicazione, o quale calendario usare, in quale valuta calcolare i prezzi, come formattare le date, ecc. Nelle prossime pagine vedremo come globalizzare gli elementi più comuni della nostra app, in modo da potersi adattare alle impostazioni e alle preferenze dell'utente.
Localizzare i testi della UI
Come si è detto, globalizzare un'app significa innanzitutto non codificare gli elementi testuali della UI direttamente nel codice XAML o C#. Per quanto riguarda in particolare le stringhe di testo, puoi spostarle direttamente in file dedicati di risorse, uno per ciascun linguaggio supportato dall'app. Un file di risorse presenta l'estensione ".resw
" e deve essere collocato in una speciale cartella all'interno del progetto denominata, per convenzione, "string".
Questa cartella deve contenere una sottodirectory per ciascun linguaggio. Il nome di ciascuna di queste sotto-directory segue il pattern denominato "BCP-47 language tag". La struttura di un tag di questo tipo prevede l'indicazione del linguaggio e, opzionalmente, della regione (ed eventualmente della variante, se presente).
Ad esempio, la sottocartella che ospita il file di risorse per i testi in italiano sarà denominata "it-IT
" (o anche solo "it
"), mentre quella per i testi in inglese sarà "en-US
", "en-GB
", "en-AU
", ecc., se vogliamo differenziare anche in base alla regione, oppure solo "en
" se vogliamo usare la stessa risorsa per le varie regioni.
Ciascuna sotto-directory ospita un file denominato Resources.resw, una nuova naming convention per le applicazioni Windows Store (è anche possibile importare in un progetto Windows Store un file .resx
da un altro progetto .NET, poiché hanno una struttura identica; basta solo copiare il file nella sotto-cartella appropriata e modificarne il nome e l'estensione).
La prossima immagine mostra un progetto Windows Store localizzato in italiano e inglese:
Visual Studio mette anche a disposizione un editor per gestire questo tipo di risorse, mostrato nella prossima immagine.
Osservando l'immagine, si notano due tipi di risorse. Il primo tipo è formato da semplici stringhe di testo, come OtherMessage
. Possiamo referenziare questo tipo di risorse da codice, come mostrato nel seguente snippet:
public MainPage()
{
this.InitializeComponent();
var resLoader = Windows.ApplicationModel.Resources.ResourceLoader.GetForCurrentView();
tbOtherMessage.Text = resLoader.GetString("OtherMessage");
}
Il secondo tipo di risorse, nell'esempio WelcomeMessage.Text
e WelcomeMessage.Width
, ha natura composita: la prima parte del nome (WelcomeMessage
) rappresenta il nome di un user control (WelcomeMessage
), mentre la seconda parte indica la proprietà del controllo cui il valore si riferisce (nell'esempio, Text
e Width
). L'infrastruttura XAML userà quindi i valori della risorsa per impostare le corrispondenti proprietà del controllo, come mostrato nel prossimo snippet.
<TextBlock x:Uid="WelcomeMessage" FontSize="18" Height="50" TextAlignment="Center" />
Il controllo TextBlock
utilizza l'attributo x:Uid per indicare la risorsa a cui quel controllo fa riferimento. Il framework XAML cercherà nel file di risorse il tipo di risorsa corrispondente (WelcomeMessage
) e, in caso positivo, utilizzerà i valori della risorsa per impostare la corrispondente proprietà del controllo.
È possibile sfruttare questo meccanismo anche per cambiare l'aspetto o la posizione di un controllo in base alla lingua dell'utente (ad esempio, per facilitare la lettura da destra a sinistra), semplicemente impostando, all'interno dei file di risorse, valori diversi per determinate proprietà.
Per testare il codice sin qui proposto possiamo utilizzare la seguente definizione XAML come riferimento:
<Page
x:Class="Demo.Html.it.GlobalizationSample.CS.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Demo.Html.it.GlobalizationSample.CS"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel Orientation="Vertical" Margin="20">
<TextBlock x:Uid="WelcomeMessage" FontSize="18" Height="50" TextAlignment="Center" />
<TextBlock x:Name="tbOtherMessage" Width="200" FontSize="18" Height="50" TextAlignment="Center" />
</StackPanel>
</Grid>
</Page>
Eseguendo l'applicazione, il testo di entrambi i controlli TextBlock
sarà visualizzato nella lingua selezionata dall'utente o, se l'utente ha selezionato una lingua non supportata dall'applicazione, direttamente nel linguaggio di default indicato nell'application manifest. Cambiando l'ordine delle lingue nelle impostazioni di Windows (Control panel Clock, Language and Region Language), il testo cambierà in base alle nuove preferenze dell'utente.
Infine, è importante ricordare come in un'applicazione Windows Store, è l'application manifest a indicare il linguaggio di default, ossia il linguaggio da utilizzare nel caso in cui l'utente abbia scelto nelle impostazioni del Control Panel un linguaggio non supportato dall'applicazione. La prossima immagine mostra il file Package.appxmanifest nel designer di Visual Studio con l'indicazione della lingua di default (en-US, in questo caso):
La localizzazione delle immagini
A volte può essere necessario localizzare non solo stringhe, ma anche immagini, ad esempio perché contengono testo o caratteri, o altre informazioni correlate a una specifica cultura come ad esempio segnali stradali. In questo caso, possiamo suddividere le immagini in sotto-cartelle specifiche, utilizzando gli stessi language tag visti per le stringhe.
Ad esempio, possiamo collocare un'immagine nella cartella images/en-US
e un'immagine con lo stesso nome, ma localizzata per una diversa lingua o cultura, nella cartella images/it-IT
. La prossima figura mostra la struttura del progetto già visto nel precedente paragrafo, con le nuove cartelle localizzate per le immagini:
Nel codice XAML possiamo quindi referenziare l'immagine semplicemente come images/sample.png
, lasciando al sistema il compito di selezionare l'immagine appropriata in base alle preferenze dell'utente. Il prossimo snippet mostra una versione modificata della pagina XAML vista in precedenza:
<!---->
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel Orientation="Vertical" Margin="20">
<TextBlock x:Uid="WelcomeMessage" FontSize="18" Height="50" TextAlignment="Center" />
<TextBlock x:Name="tbOtherMessage" Width="200" FontSize="16" Height="50" TextAlignment="Center" />
<Image Source="images/sample.png" Width="300" />
</StackPanel>
</Grid>
<!---->
Il risultato è mostrato nella seguente immagine:
Teniamo inoltre presente che è possibile specificare informazioni aggiuntive (qualifier), come fattori di scala e modalità di contrasto. Il seguente snippet mostra un esempio di utilizzo di un'immagine a contrasto elevato con sfondo bianco e colore di primo piano nero:
images/en-US/sample.scale-100_contrast-white.png
Anche in questo caso, per utilizzare l'immagine è sufficiente questo codice:
Source="images/sample.png"
Sarà infatti il Resource Manager a preoccuparsi di localizzare la giusta immagine sulla base delle preferenze dell'utente.
Formattare date e orari
Quando si ha a che fare con date e orari, le possibili impostazioni sono molteplici. Per questo motivo sarebbe bene non mostrare mai una data o un orario a schermo senza tenere conto delle preferenze dell'utente.
A questo fine puoi utilizzare la classe Windows.Globalization.DateTimeFormatting.DateTimeFormatter
. Questa classe semplifica la localizzazione di date e orari in base alle preferenze dell'utente. Quel che serve, una volta istanziato un nuovo oggetto di tipo DateTimeFormatter
, è una stringa che descriva il tipo di formattazione desiderata. A questo punto è sufficiente invocare il metodo Format e passare come parametro la data da formattare, come mostrato nel prossimo snippet:
public MainPage()
{
this.InitializeComponent();
var resLoader = new Windows.ApplicationModel.Resources.ResourceLoader();
tbOtherMessage.Text = resLoader.GetString("OtherMessage");
// dichiarare un formato e applicarlo alla data
DateTimeFormatter shortDateformatter = new DateTimeFormatter("shortdate");
tbLocalizedDate.Text = shortDateformatter.Format(DateTime.Now);
}
Per visualizzare il risultato, aggiungiamo una nuova TextBlock
alla pagina XAML, come mostrato nel seguente snippet:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel Orientation="Vertical" Margin="20">
<TextBlock x:Uid="WelcomeMessage" FontSize="18" Height="50" TextAlignment="Center" />
<TextBlock x:Name="tbOtherMessage" Width="200" FontSize="16" Height="50" TextAlignment="Center" />
<Image Source="images/sample.png" Width="300"></Image>
<!-- usiamo la tbLocalizedDate nel blocco di testo -->
<TextBlock x:Name="tbLocalizedDate" FontSize="18" Margin="0 10" Width="300" Height="50" TextAlignment="Center"></TextBlock>
</StackPanel>
</Grid>
Windows.Globalization.DateTimeFormatting
mette a disposizione svariati template per formattare date e orari in base alla lingua e alla cultura dell'utente, come "shortdate", "shorttime", "dayofweek", e così via. La data verrà quindi formattata sulla base delle preferenze dell'utente, grazie al fatto che ciascun template si basa su una serie di pattern specifici per la cultura e la lingua dell'utente. È possibile ispezionare gli elementi costitutivi utilizzati per costruire i vari pattern, grazie alla proprietà Patterns
esposta dalla classe DateTimeFormatting
. Prendiamo ad esempio il template "month day":
DateTimeFormatter monthDayformatter = new DateTimeFormatter("month day");
var monthDayPattern = monthDayformatter.Patterns;
A seconda della lingua e della cultura dell'utente, i pattern utilizzati per localizzare la data saranno diversi:
En-US: "{month.full} {day.integer}"
it-IT: "{day.integer} {month.full}"
È anche possibile utilizzare gli elementi costitutivi di un template direttamente, come mostrato nel seguente snippet.
DateTimeFormatter monthDayformatter = new DateTimeFormatter("{month.full} {day.integer}")
Per una carrellata completa dei template e relativi costituenti, si rinvia alla documentazione MSDN
Numeri e valute
Quando si ha a che fare con numeri e valute, il namespace Windows.Globalization.NumberFormatting
mette a disposizione una serie di classi helper per formattare questi valori tenendo conto della lingua e della cultura. Le classi sono le seguenti:
Classe | Descrizione |
---|---|
CurrencyFormatter | per formattare valute monetarie |
DecimalFormatter | per formattare numeri decimali |
PercentFormatter | per formattare percentuali |
PermilleFormatter | per formattare numeri con tre o più cifre |
Il seguente snippet mostra un esempio di come formattare un importo nella valuta corrispondente alla cultura dell'utente:
public MainPage()
{
this.InitializeComponent();
var resLoader = new Windows.ApplicationModel.Resources.ResourceLoader();
tbOtherMessage.Text = resLoader.GetString("OtherMessage");
DateTimeFormatter shortDateformatter = new DateTimeFormatter("shortdate");
tbLocalizedDate.Text = shortDateformatter.Format(DateTime.Now);
// formato delle vallute
var userCurrency = GlobalizationPreferences.Currencies[0];
var currencyFormat = new Windows.Globalization.NumberFormatting.CurrencyFormatter(userCurrency);
var currencyValue = 1234.567;
currencyFormat.ApplyRoundingForCurrency(RoundingAlgorithm.RoundHalfUp);
tbLocalizedCurrency.Text = currencyFormat.Format(currencyValue);
}
Il codice recupera le informazioni sulla valuta dalle preferenze dell'utente, quindi istanzia un nuovo formato passando come parametro la stringa che rappresenta la valuta stessa. Il metodo ApplyRoundingForCurrency
permette infine di arrotondare la cifra utilizzando uno degli algoritmi indicate dall'enum RoundingAlgorithm
.
Per testare questo codice, aggiungiamo un nuovo controllo TextBlock
alla pagina, come mostrato qui di seguito:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel Orientation="Vertical" Margin="20">
<TextBlock x:Uid="WelcomeMessage" FontSize="18" Height="50" TextAlignment="Center" />
<TextBlock x:Name="tbOtherMessage" Width="200" FontSize="16" Height="50" TextAlignment="Center" />
<Image Source="images/sample.png" Width="300"></Image>
<TextBlock x:Name="tbLocalizedDate" FontSize="18" Margin="0 10" Width="300" Height="50" TextAlignment="Center"></TextBlock>
<!-- blocco di testo per le valute-->
<TextBlock x:Name="tbLocalizedCurrency" FontSize="18" Margin="0 10" Width="300" Height="50" TextAlignment="Center"></TextBlock>
</StackPanel>
</Grid>
Il risultato è mostrato qui di seguito:
Lavorare con i calendari
Quando si lavora con date e orari, la classe Windows.Globalization.Calendar si fa carico delle conversioni tra un calendario e l'altro (tieni presente che calendari diversi da quello gregoriano calcolano diversamente giorni e mesi), nonché degli aggiustamenti derivanti dall'ora legale o dagli anni bisestili.
Il seguente codice mostra alcune informazioni relative al calendario sulla base delle preferenze dell'utente:
public MainPage()
{
this.InitializeComponent();
...
var userCalendar = GlobalizationPreferences.Calendars[0];
var userClock = GlobalizationPreferences.Clocks[0];
var userLanguages = GlobalizationPreferences.Languages;
var cal = new Windows.Globalization.Calendar(userLanguages, userCalendar, userClock);
cal.SetToNow();
tbLocalizedCalendar.Text = cal.DayOfWeekAsString() + " " + cal.DayAsPaddedString(2) + " " + cal.MonthAsString() + " " + cal.YearAsString();
}
Una volta creata un'istanza della classe Calendar, è possibile compiere qualunque operazione sulle date, come aggiungere secondi, minuti, ore, giorni, mesi o anni. Espone anche informazioni su quanti giorni ci sono in un anno e quante ore in un giorno. Ad esempio, il seguente snippet mostra come aggiungere un mese a una data (ad esempio per calcolare una scadenza).
var cal = new Windows.Globalization.Calendar();
var endDate = cal.Clone();
endDate.AddMonths(1);
Il Multilingual App Toolkit
Per finire, merita di essere accennato il Multilingual App Toolkit, una estensione per Visual Studio messa a punto da Microsoft per facilitare la globalizzazione di un'applicazione Windows Store e gestire le traduzioni.
Dopo aver creato un file di risorse di default per l'applicazione, possiamo utilizzare il toolkit per tradurre la nostra app in altre lingue. Una volta installato il toolkit e selezionato il progetto per cui si vuole adoprare lo strumento nel Solution Explorer, nel menu di Visual Studio occorre andare su Tools e selezionare Enable Multilingual App Toolkit, come mostrato nella prossima immagine:
Una volta abilitato lo strumento per il progetto corrente, Visual Studio aggiungerà una nuova cartella, con all'interno un file denominato <NomeApp>_qps-ploc.xfl, come mostrato nella seguente immagine.
A questo punto, per aggiungere una traduzione occorre cliccare con il tasto destro sul progetto nella Solution Explorer e, nel relativo menu contestuale, selezionare la voce di menu Add translation languages, evidenziato nella prossima immagine:
Si aprirà quindi una finestra con un elenco di lingue/regioni tra cui selezionare quelle per le quali vogliamo aggiungere una traduzione. Una volta ricompilato il progetto, il toolkit è pronto per essere usato. La prossima immagine mostra la relativa interfaccia:
I vantaggi derivanti dall'uso del Multilingual App Toolkit sono molteplici:
- Facilita la gestione delle modifiche relative alle risorse e lo stato della traduzione durante lo sviluppo.
- Offre un'interfaccia utente per la scelta delle lingue in base ai provider di servizi di traduzione configurati.
- Supporta il formato di file XLIFF standard nel campo della localizzazione.
- Offre un motore per le pseudo lingue che agevola l'individuazione dei problemi di traduzione durante lo sviluppo.
- Si connette a Microsoft Language Portal per accedere facilmente alle stringhe tradotte e alla terminologia.
Puoi trovare tutti i dettagli sulla configurazione e sull'utilizzo delle varie feature dello strumento su MSDN