multilingual wpf application - c#

I have a WPF application (in English) and I would like to let users to select different languages. I have read some possibilities to change languages in runtime applications, but I only want to choose a language during installation time and never change it.
Do you think the fastest and easiest way to do it is developing different versions of the program (changing only text language) and let the user to select one of them during the installation?? Probably to repeat code only changing textbox or labels is not very elegant, but notice that I have the application finished in English and I don´t need to change language at runtime.

You can follow these steps:
Creating the resource files
Add this file StringResources.xaml to Resources directory. Here is an example:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<system:String x:Key="close">Close</system:String>
</ResourceDictionary>
You can create several files, one for each language.
Adding the resource (Call this when you start your application)
private void SetLanguageDictionary()
{
ResourceDictionary dict = new ResourceDictionary();
switch (Thread.CurrentThread.CurrentCulture.ToString())
{
case "en-US":
dict.Source = new Uri("..\\Resources\\StringResources.xaml", UriKind.Relative);
break;
case "fr-CA":
dict.Source = new Uri("..\\Resources\\StringResources.fr-CA.xaml", UriKind.Relative);
break;
default :
dict.Source = new Uri("..\\Resources\\StringResources.xaml",UriKind.Relative);
break;
}
this.Resources.MergedDictionaries.Add(dict);
}
Using the Resource, like this -
<Button
x:Name="btnLogin"
Click="btnLogin_Click"
Content="{DynamicResource close}"
Grid.Row="3"
Grid.Column="0"
Padding="10" />
Source: https://www.codeproject.com/Articles/123460/Simplest-Way-to-Implement-Multilingual-WPF-Applica

I think the solution proposed by Aghilas is good; but you can use StaticResource instead of using DynamicResource in step 3, DynamicResource is not required in your case as you are not going to chnage the language while application is running.
Also have a look at these articles having details about using Resx files for localization in WPF -
Localizing a WPF Application with ResX Files
WPF Localization
WPF Localization Guidance - Whitepaper

Just to improve #AghilasYakoub's correct answer, I think I need to point out that the following code should be added to the file App.xaml apart from what he had said:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/StringResources.xaml"/>
<ResourceDictionary Source="Resources/StringResources.fr-CA.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>

If you want to use RESX files instead of resource dictionaries, you can do it easily with static references in XAML.
<Window x:Class="MyApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:res="clr-namespace:MyApp.Resources">
<Button Text="{x:Static res:MainWindow.MyTestKey}">
</Window>
In the Resource folder is the MainWindow.resx, MainWindow.de.resx, etc. and every file contains a key MyTestKey with a translation.

Related

How to load a specific language XAML file

I am building my language system using XAML files.
The code I have is as follows:
es.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyAppTest"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<system:String x:Key="Hello">Hola</system:String>
</ResourceDictionary>
en.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyAppTest"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<system:String x:Key="Hello">Hello</system:String>
</ResourceDictionary>
App.xaml
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary x:Name="en" Source="mylang/en.xaml"/>
<ResourceDictionary x:Name="es" Source="mylang/es.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Main.xaml
<TextBlock Text="{DynamicResource Hello}" />
<ComboBox>
<ComboBoxItem Content="English" />
<ComboBoxItem Content="Spanish" />
</ComboBox>
How can I read the language XAML file according to the language selected in the ComboBox?
What you are trying to do is possible by using DynamicResource like you did for TextBlock:
<TextBlock Text="{DynamicResource Hello}"/>
The DynamicResource markup extension will reslove a resource at runtime and update it if it was added, removed or replaced in a resource dictionary. You would create a resource dictionary for each language and you would put them into the application resource dictionary.
Since you cannot assign multiple objects to the same key in a resource dictionary, you should only have the resource dictionary for the currently applied language in your application resources. Applying and switching languages is done in code by exchanging the current language resource dictionary.
In order to set the initial language, you can load your language setting and add the corresponding language resource dictionary to the application resources in the App.xaml.cs class. Use a URI and set it as source of the resource dictionary. I assume it is english in this sample.
var languageResourceDictionaryUri = new Uri("mylang/en.xaml");
var languageResourceDictionary = new ResourceDictionary { Source = languageResourceDictionaryUri };
Application.Current.Resources.MergedDictionaries.Add(languageResourceDictionary);
You should store a reference to the current language resource dictionary, so you can remove it from the application resources, when the user changes the language using the ComboBox.
Application.Current.Resources.MergedDictionaries.Remove(languageResourceDictionary);
Then you would add the a new resource dictionary for the selected language as above. Depending on where you want to switch the language, you might want to create a service to hide the access to the global, static application instance that is needed to change the resources.

How to use resources (fonts, images, styles) from external library

I'm having problems with loading resources (images, font families, styles) from an external resource dll. I would like to have all resources (images, fonts, styles...) in one resource dll and set them to be accessible over all projects (libraries) in this solution. Those projects in solution are different libraries that are referenced and called by main application.
So far i tried several different propositions how to do it but none of them work.
I'm using Visual studio 2019 and compiling for .net 4.6.2 - if that means something...
First I created an resource library called myapp.resources. Inside this project I have a folder named Fonts and inside that folder is the Lato-Thin font.
Also at the root of the project I created a resource dictionary called Fonts.xaml
-Project
-Fonts
-Lato-Thin.ttf
-Fonts.xaml
the structure of the Fonts.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:myapp.resources">
<FontFamily x:Key="LatoThin">Fonts/#Lato Thin</FontFamily>
</ResourceDictionary>
In the main app App.xaml I added the loading of that ResourceDictionary:
<ResourceDictionary x:Key="Dict">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/myapp.resources;component/Fonts.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
The last step is adding this fontfamily to label control:
<Label Grid.Row="0" Grid.Column="0" Content="some random text for test" FontFamily="{StaticResource ResourceKey=LatoThin}"/>
But I always get the error that the LatoThin font resource can't be located.
The same error I get with any other resource type like images, styles.
Of course I added the references to projects and every file is where it should be.
The only thing that is working is, for example adding image to buttons in this way.
<syncfusion:ButtonAdv x:Name="btnSelectFile"
Grid.Row="1" Grid.Column="2"
VerticalAlignment="Center" SizeMode="Small"
Height="26" Width="26" Label="Button" Margin="3,3,3,3"
SmallIcon="pack://application:,,,/myapp.resources;component/Images/add.png"/>
I tried to find some complete tutorials to follow but I always have the same error.
So, my question would be: How to solve this?
Thanks for any advice.
Make sure your font file (Lato-Thin.ttf) has its build action set to Resource.
In your Fonts.xaml make sure to use this format:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:myapp.resources">
<FontFamily x:Key="LatoThin">/myapp.resources;component/Fonts/#Lato Thin</FontFamily>
</ResourceDictionary>
Tip: You can remove pack://application:,,, from your URIs to make them shorter.

What kind of "Key" do I need to supply when loading Resource Dictionaries?

In trying to set a default ResourceDictionary I receive the following warning:
The designer does not support loading dictionaries that mix
'ResourceDictionary' items without a key and other items in the same
collection. Please ensure that the 'Resources' property does not
contain 'ResourceDictionary' items without a key, or that the
'ResourceDictionary' item is the only element in the collection.
This is the code that I am using in my App.xaml file, that received the above warning:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Lang.en-US.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
This is the exact same code that I've used to set a ResourceDictionary in Visual Studio 2008. I am now using VS 2010. What Key do I need to provide this ResourceDictionary for it to work correctly?
This is the line in my MainWindow.xaml that I am currently testing along with this code:
<MenuItem Header="{DynamicResource new_test}" />
Since you haven't posted your complete XAML file, i suspect there are other resources apart from merged dictionary in your resources section.
As per MSDN -
It is legal to define resources within a ResourceDictionary that is
specified as a merged dictionary, either as an alternative to
specifying Source, or in addition to whatever resources are included
from the specified source. However, this is not a common scenario; the
main scenario for merged dictionaries is to merge resources from
external file locations. If you want to specify resources within the
markup for a page, you should typically define these in the main
ResourceDictionary and not in the merged dictionaries.
Try moving other resources in separate resource dictionary and make sure all other resources have x:Key set on them -
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Lang.en-US.xaml" />
</ResourceDictionary.MergedDictionaries>
<ResourceDictionary>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<ContextMenu x:Key="MyContextMenu"/>
</ResourceDictionary>
</ResourceDictionary>
</Application.Resources>
Use resource file for translations. Its better than resource dictionary.
Here is an example:
Set prefix like this for usage in xaml.
xmlns:const="clr-namespace:FileExplorer.Properties"
Resources are located in properties.
To use them in XAML you will need following:
<TextBox Text="{x:Static const:Resources.Window_Title_String}"/>
If you have different languages then create for each language own resource file following naming convention.
For example:
Resources.resx (this will be default)
Resources.de-DE.resx (this is for german)
Now you just have to set current culture to german for your app to be on german and the proper resource file will be used automatically.
Like this in Main method:
Thread.CurrentThread.CurrentUICulture = new CultureInfo("de-DE");

How to start an internationalized WPF-Project in SharpDevelop 4.2?

I want to create a Software, where the User can choose between several Languages.
As a start i want to learn how to handle Internationalization, since i have never done that before.
As IDE i use SharpDevelop or #develop, however you would spell it.
I want to use C# and WPF, since i'm also learning XAML/WPF at the moment.
So i create a new WPF-Project in ShardDevelop.
On the Main Window i create a ComboBox and a TextBlock.
The ComboBox get's two Entries: "German" and "English".
The textBlock should show "Hallo Welt!" or "Hello World!", depending on the Language which is selected.
Now comes the part where i get stuck.
I guess each language get's a separate file in XML/XAML-Style (Makes sense).
Where are these files and how are they and their Content loaded so that the Text of the selected Language is loaded?
I found several examples but all are something about creating Resource-DLL and using some weird program to decompile them back into a csv-file... i don't get it, isn't there an easier way?
I took the next Step.
The Text of the TextBlock is now loaded via "{StaticResource Strings.MainForm.hwText}". It looks like this now:
<TextBlock Text="{StaticResource Strings.MainForm.hwText}" />
Also I created one ResourceDictionary for German and one for English which both define the key i used in the TextBlock.
In the Application.Resources Part i load one of the ResourceDictionary's per default.
The Problem now is: How can i "unload" this Dictionary during Runtime and Replace it with the other?
Of course i use the SelectionChange-Event of the ComboBox, but what do i do there?
Problem solved!! Thanks to kmatyaszek
Although i changed the Code of the Event-Handler a bit to my needs:
Uri baseUri = new Uri(AppDomain.CurrentDomain.BaseDirectory);
Uri uri = new Uri(baseUri,"Languages\\lang."+((sender as ComboBox).SelectedItem as ComboBoxItem).Tag.ToString()+".xaml");
if(File.Exists(uri.LocalPath) || File.Exists((uri = new Uri(baseUri,"Languages\\lang.de-DE.xaml")).LocalPath)){
ResourceDictionary dict = new ResourceDictionary();
dict.Source = uri;
this.Resources.MergedDictionaries.Add(dict);
}
If you created two ResourceDictionary files you can binding by DynamicResource.
Example:
First resource file (Lang.en-US.xaml):
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<system:String x:Key="Username">Username:</system:String>
<system:String x:Key="Password">Password:</system:String>
<system:String x:Key="close">Close</system:String>
<system:String x:Key="login">Login</system:String>
</ResourceDictionary>
Second resource file (Lang.pl-PL.xaml):
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<system:String x:Key="Username">Login:</system:String>
<system:String x:Key="Password">Hasło:</system:String>
<system:String x:Key="close">Zamknij</system:String>
<system:String x:Key="login">Zaloguj</system:String>
</ResourceDictionary>
Set default language in Application resources:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Lang.en-US.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Let's say that we have ComboBox like below:
<ComboBox Name="cbLang" Margin="2" SelectionChanged="cbLang_SelectionChanged" >
<ComboBoxItem Content="English" Tag="en-US" />
<ComboBoxItem Content="Polish" Tag="pl-PL" />
</ComboBox>
Code-behind SelectionChanged:
private void cbLang_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
ResourceDictionary dict = new ResourceDictionary();
switch (((sender as ComboBox).SelectedItem as ComboBoxItem).Tag.ToString())
{
case "en-US":
dict.Source = new Uri("Lang.en-US.xaml", UriKind.Relative);
break;
case "pl-PL":
dict.Source = new Uri("Lang.pl-PL.xaml", UriKind.Relative);
break;
default:
break;
}
this.Resources.MergedDictionaries.Add(dict);
}
And you can binding like this:
<TextBlock Text="{DynamicResource Username}" VerticalAlignment="Center" />

WPF Prism - Where to put Resources?

I have a prism application and various modules. I am wondering where is the best place to locate resources such as styles, brush, controltemplates, datatemplates?
Should I make one single resource dictionary and put everything there? Should each module have their own resources? Or each view? I would like to follow the Prism goal of keeping everything modular, but also I dont see the point in re-declaring the same resources in every module...
I develop application with Prism, and I use technique very close to described in Prism's manual. There is YourApplication.Infrastructure project, where you usually place all your shared interfaces etc. So:
I just add project YourApplication.Resources
Create there folder Themes
Create separate xaml file in Themes folder for each group of resources (like Generic.WPF.xaml for standard WPF controls' styles, Generic.Brushes.xaml for brushes etc.)
Create file Themes\Generic.xaml (exactly with this name, it will add huge benefits in the future) with content like
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Generic.Brushes.xaml"/>
<ResourceDictionary Source="Generic.WPF.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
Now you can add those resources in any module (you have separate project for it, right?) by adding reference to YourApplication.Resources to that project and adding to your view's xaml:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/YourApplication.Resources;component/Themes/Generic.xaml"/>
</ResourceDictionary.MergedDictionaries>
<!-- Put your not shared resource here -->
</ResourceDictionary>
</UserControl.Resources>
I don't know, maybe this way has some problems, but it works, and works well for me. If anybody can comment somehow this way (pros/cons) - I will be very happy to hear it!
Application-wide resources I usually put in a ResourceDictionary, which is added to either App.xaml or StartupWindow.xaml
Resources for a specific View are usually located with the View. For example, a UserControl that is being used for a CalendarView will contain any custom resources for the Calendar, such as calendar-specific brushes, styles, templates, etc.
I usually don't see a reason to make module-wide resources, but if I ever do I'd have a ResourceDictionary for the Module which can be loaded into the app's merged dictionaries at runtime, or included in individual Views in the Module.
I would like to share some new knowledges. I am using #chopikadze approach. And it is really cool approach. Thanks to you!
However, if you do not want write every time for each control these piece of code:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/YourApplication.Resources;component/Themes/Generic.xaml"/>
</ResourceDictionary.MergedDictionaries>
<!-- Put your not shared resource here -->
</ResourceDictionary>
</UserControl.Resources>
Then you can just declare <ResourceDictionary/> in App.xaml of your Bootstrapper like that:
<Application.Resources>
<ResourceDictionary Source="pack://application:,,,/YourApplication.Resources;component/Themes/Generic.xaml"/>
</Application.Resources>

Categories