|
I need to support multi language for an application. The language sources are stored in separate language files where each word in the files has a unique indexer.
For example: In a label I would like to display the word:
* "pants" when selecting US-English as language
* "trousers" when selecting UK-English as language
The US file looks like this:
1=pants
2=truck
The UK file looks like this:
1=trousers
2=lorry
Notice both file contains the same indexes
I use two buttons to select language:
<Button Click="btnUS_Click" Content="US"></Button>
<Button Click="btnUK_Click" Content="UK"></Button>
With the Click event connected to each button I'm able to read the appropriate language file.
The label I want to display "pants/trousers" is just:
<Label Name="lblWord"></Label>
In the XAML code I would like to give the the label it's unique index (in this case 1). On way is to store it in the Tag property:
<Label Name="lblWord" Tag="1"></Label>
But I'm not sure if this is the best way.
Now for my question, how do I display the correct word in the label depending on the selected language and the stored index for it?
|
|
|
|
|
I would use a custom class that contains a property for the index and another property for the actual word. Something like this:
class LanguageItem
{
public int ID { get; set; }
public string Word { get; set; }
}
Then you could create a property in your viewmodel/codebehind that returns an ObservableCollection<LanguageItem> like this
ObservableCollection<LanguageItem> words = new ObservableCollection<LanguageItem>();
public ObservableCollection<LanguageItem> Words
{
get
{
return words;
}
set
{
if (words != value)
{
words = value;
NotifyPropertyChanged("Words");
}
}
}
You could populate this with with a method that reads and parses your language file appropriately.
Then you can bind your individual controls to the correct LanguageItem's Word like this
<Label Content="{Binding Path=Words[0].Word}"/>
<Label Content="{Binding Path=Words[1].Word}" Grid.Column="1"/>
Hope this helps
Live for today. Plan for tomorrow. Party tonight!
|
|
|
|
|
Where/how is the "NotifyPropertyChanged" defined? I do every thing in a Window class and there is that method not defined
|
|
|
|
|
To bind to a property in your codebehind/viewmodel you need to implement this Interface. Here[^] is the MSDN page for this interface.
Hope this helps
Live for today. Plan for tomorrow. Party tonight!
|
|
|
|
|
Still nothing happens. I must be missing something. Here is the entire code:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.IO;
namespace SwitchLanguage
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
string folderPath = @"C:\Users\SwitchLanguage\";
ObservableCollection<LanguageItem> words;
public event PropertyChangedEventHandler PropertyChanged;
public MainWindow()
{
InitializeComponent();
PropertyChanged += new PropertyChangedEventHandler(MainWindow_PropertyChanged);
}
private void btnUK_Click(object sender, RoutedEventArgs e)
{
ReadFile("UK.txt");
}
private void btnUS_Click(object sender, RoutedEventArgs e)
{
ReadFile("US.txt");
}
private void ReadFile(string file)
{
if (!File.Exists(folderPath + file))
{
throw new FileNotFoundException("Language file doesn't exist");
}
StreamReader sr = new StreamReader(folderPath + file);
ObservableCollection<LanguageItem> words = new ObservableCollection<LanguageItem>();
while (!sr.EndOfStream)
{
string[] parts = sr.ReadLine().Split('=');
LanguageItem languageItem = new LanguageItem();
languageItem.ID = int.Parse(parts[0]);
languageItem.Word = parts[1];
words.Add(languageItem);
}
Words = words;
sr.Close();
}
public ObservableCollection<LanguageItem> Words
{
get { return words; }
set
{
if (words != value)
{
words = value;
PropertyChanged(this, new PropertyChangedEventArgs("Words"));
}
}
}
void MainWindow_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
Console.WriteLine("");
}
}
public class LanguageItem
{
public int ID { get; set; }
public string Word { get; set; }
}
}
<Window x:Class="SwitchLanguage.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Label Name="lblWord" Content="{Binding Path=Words[0].Word}" Margin="50,-100,0,0" Width="200" Height="30"></Label>
<Button Width="100" Height="40" Click="btnUK_Click" Content="UK" Margin="-100,0,0,0"></Button>
<Button Width="100" Height="40" Click="btnUS_Click" Content="US" Margin="100,0,0,0"></Button>
</Grid>
</Window>
|
|
|
|
|
Well, the most obvious thing here is that you haven't set the DataContext of your window to the Window class. To be honest, what you have here is not the way I would have chosen to do it, but if it eventually works for you...
Anyway all you need to do is, after the call to InitializeComponent, add
DataContext = this;
|
|
|
|
|
Thanks! That did the trick
Pete, would you have solve this?
I don't like to use index assigners in the XAML code Words[0].Word, but I don't see any other solution.
In my case I cannot use resource files. Resource file (what I have seen) must be compiled into the application. To use a text file is much easier since I can get as many languages I want if all files follow the correct format.
|
|
|
|
|
We use a database to store the text, and this is bound in at runtime. It's that straightforward.
|
|
|
|
|
Hi Pete,
Just a quick question on this topic. You said that you store you dictionaries in a database, and I can see the value in this, but you also said you bind the values at runtime. Does this mean that you have a property for each and every label, group box header, tab header etc that needs to be localised, and then bind to the property, or do you store your localising (is that a word?) strings in some sort of collection? If it is the former, then this must be a hell of a job, as on some forms, you could have up to 20 - 25 labels etc that need to be localised, and so would need to declare that many properties in your vewmodel, and if it is the latter, how do you bind the labels etc to the collection ? The way I proposed to the OP, i.e using the indexers, I got from one of Sasha Barbers articles WPF:Binding to individual collection items[^], and would appreciate it if you could give me your thought on this, as it does seem a bit of a hack, and could potentially lead to disastrous results if your dictionaries are not in sync.
Thanks for your time.
Wayne.
Live for today. Plan for tomorrow. Party tonight!
|
|
|
|
|
We use a custom localisable MarkupExtension to handle this. The beauty of storing the values in the database is that, with a simple query, we can find any items that haven't been localised without needing to run up the application.
|
|
|
|
|
Tish - that's the sound of a light bulb switching on in my head. You certainly know your stuff! Many thanks.
Live for today. Plan for tomorrow. Party tonight!
|
|
|
|
|
No problem. Glad to help.
|
|
|
|
|
Pete O'Hanlon wrote: We use a custom localisable MarkupExtension to handle this
And here I was going to ask for an example of the binding syntax!
Never underestimate the power of human stupidity
RAH
|
|
|
|
|
This is how I would do it (only if my requirements stated I had to use your language files - otherwise I would use Resource Files as Abhinav suggestes)
Your LanguageItem class:
class LanguageItem
{
public int ID { get; set; }
public string Word { get; set; }
}
A separate class which I will call LanguageViewModel:
class LanguageViewModel : INotifyPropertyChanged
{
enum LanguageChoice
{
UK,
US
}
private LanguageChoice selectedLanguage = LanguageChoice.UK;
private string languageText = "Switch to US English";
public string LanguageText
{
get
{
return languageText;
}
set
{
if (languageText != value)
{
languageText = value;
NotifyPropertyChanged("LanguageText");
}
}
}
ObservableCollection<LanguageItem> words = new ObservableCollection<LanguageItem>();
public ObservableCollection<LanguageItem> Words
{
get
{
return words;
}
set
{
if (words != value)
{
words = value;
NotifyPropertyChanged("Words");
}
}
}
public LanguageViewModel()
{
LoadLanguage(selectedLanguage);
}
private RelayCommand switchCommand;
public ICommand SwitchCommand
{
get
{
return switchCommand ?? (switchCommand = new RelayCommand(() => ObeySwitchCommand()));
}
}
private void ObeySwitchCommand()
{
selectedLanguage = selectedLanguage == LanguageChoice.UK ? LanguageChoice.US : LanguageChoice.UK;
LanguageText = selectedLanguage == LanguageChoice.UK ? "Switch to US English" : "Switch to UK English";
LoadLanguage(selectedLanguage);
}
void LoadLanguage(LanguageChoice languageToLoad)
{
string path;
if (languageToLoad == LanguageChoice.UK)
{
path = @"C:\Users\SwitchLanguage\UK.txt";
}
else
{
path = @"C:\Users\SwitchLanguage\US.txt";
}
StreamReader sr = new StreamReader(path);
ObservableCollection<LanguageItem> newWords = new ObservableCollection<LanguageItem>();
while (!sr.EndOfStream)
{
string[] parts = sr.ReadLine().Split(new char[] { ',' });
int index;
int.TryParse(parts[0], out index);
LanguageItem li = new LanguageItem() { ID = index, Word = parts[1] };
newWords.Add(li);
}
Words = newWords;
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(String info)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(info));
}
}
#endregion
}
My XAML File (Note no code in code behind):
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Label Content="{Binding Path=Words[0].Word}"/>
<Label Content="{Binding Path=Words[1].Word}" Grid.Column="1"/>
<Button Grid.Row="1" Grid.Column="1" HorizontalAlignment="Right" VerticalAlignment="Bottom"
Command="{Binding Path=SwitchCommand}" Content="{Binding Path=LanguageText}"
Width="100" Height="23"/>
Then I would override the OnStartUp method in the App.xaml.cs file like this:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
LanguageViewModel vm = new LanguageViewModel();
LanguageTest lt = new LanguageTest();
lt.DataContext = vm;
lt.ShowDialog();
}
And here is a bog standard RelayCommand class that is used for binding the Button's click command to the SwitchCommand on the ViewModel:
public class RelayCommand : ICommand
{
readonly Action execute;
readonly Func<bool> canExecute;
public RelayCommand(Action execute)
: this(execute, null)
{
}
public RelayCommand(Action execute, Func<bool> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
this.execute = execute;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return canExecute == null ? true : canExecute();
}
public event EventHandler CanExecuteChanged
{
add
{
if (canExecute != null)
CommandManager.RequerySuggested += value;
}
remove
{
if (canExecute != null)
CommandManager.RequerySuggested -= value;
}
}
public void Execute(object parameter)
{
execute();
}
}
Live for today. Plan for tomorrow. Party tonight!
|
|
|
|
|
I would look at using a resource file for maintaining different languages.
See WPF Localization Using RESX Files[^] for an example.
Too much of heaven can bring you underground
Heaven can always turn around
Too much of heaven, our life is all hell bound
Heaven, the kill that makes no sound
modified on Thursday, September 1, 2011 7:23 AM
|
|
|
|
|
With WCF you can retrieve data, etc into silverlight UI using asyncronous calls...
Then is there any reason for backgrooundworker process to be used in silverlight?
Thanks
modified on Thursday, September 1, 2011 1:28 AM
|
|
|
|
|
Define required! I am yet to use a BGW process in SL.
Never underestimate the power of human stupidity
RAH
|
|
|
|
|
Hello,
I updated the question.
Thanks
|
|
|
|
|
I'm sure you've asked this question before. Yes, you can use a background worker to do something in Silverlight, but in most cases it's not necessary because of the asynchronous nature of the client/server roundtrip. Saying that, you could be attempting to do something entirely on the client side, e.g. performing a long running calculation, and you should use some form of threading for this.
The answer you are looking for:
Roundtrip = asynchronous = no need for additional threading
Client side only = synchronous = you might want to use threading here.
|
|
|
|
|
In that case, on the client side, to use threading, I can use background worker? and not manually creating threads like thread t = new thread(...)?
Thanks
|
|
|
|
|
arkiboys wrote: on the client side, to use threading, I can use background worker
Yes.
|
|
|
|
|
I see.
So in silverlight, I do not need to learn how to do manual threading as long as I know backgroundworker process?
Thanks
|
|
|
|
|
That's correct. Learning manual threading is good knowledge to have, it will make you a better coder, but it isn't absolutely necessary.
|
|
|
|
|
I see.
This is now clear.
Thanks
|
|
|
|
|
To be able to develop in windows phone, as well as having the visual studio express 2010, should I also install the VS.NET 2010 Express for Windows Phone
Thanks
|
|
|
|