3

I have multi languages application who runs in few languages. In my application is have text who user can change, but default text also is need to translate in different languages. This mean if user want to edit some text is need to edit resx file for his language.

First I try with ResourceWriter class and then my resx file is broke. And I created again.

Second I try with ResXResourceWriter class but I can't find good method who give me this functionality (to edit value of some property).

I think just read the resx file like xml and edited, but then I don't know how can make application is build again.

Is it possible to edit value in resx from application in runtime. And after that is it possible to build application with c# code like is in build in visual studio.

EDIT I don't want to change resx files from .en.resx to somethink else (.de.resx). I want to change values in this resx file. For example I have key who is btnCont with value Click. I want to change btnCont to be with new value Click me, for example. And after that is to generate again the dll for changed resx file.

Thanks in advice

Evgeni Velikov
  • 362
  • 3
  • 10
  • 28
  • 1
    .resx are not meant to be changed by end users, they are compiled into assemblies. You would have to recompile satellite assemblies (assemblies in directories like en-us, fr-fr, etc.) on the fly. Is this what you want to do? even though, you'd have to restart your application. – Simon Mourier Feb 22 '16 at 09:05
  • Ok no problem to restart application. But I see the application who some "organization" start to translate the application. Is it possible the user can make some resx file and then to build to dll and use some own language for the application. – Evgeni Velikov Feb 22 '16 at 11:57
  • 1
    It's possible yet, check out this thorough article here: http://www.informit.com/articles/article.aspx?p=414984&seqNum=9 and tell us if it's something that helps (see to build a DLL, you'll need the AL.exe tool, demonstrated at the end). – Simon Mourier Feb 22 '16 at 12:28
  • I did something like that (without the compilation part - I was adding the generated code manualy to the project). First you need to decompile dll to get resources (I can find a code for that part for you, if you wish). Than you need an editor, which on SaveAndCompile would do some validation (e.g. you may need to replace special characters). Than you'll have to construct correct files based on the given set of keywords and values. Compiling it shouldn't be too difficult since we have Roslyn. All of that is a bit time consuming, but doable. – Maciek Świszczowski Feb 23 '16 at 12:08
  • I think it is dublicate with http://stackoverflow.com/questions/676312/modifying-resx-file-in-c-sharp – Sercan Timoçin Feb 26 '16 at 12:04

2 Answers2

0

Maybe there are better ways to do this (and I would love to learn about them), but what I do when I need to change the language in runtime is to add all localizable strings as readonly properties to my viewmodel.

    public string ImportString { get { return lang.Import; } }

Then I just add a binding to this string from XAML and after changing the UI culture I call OnPropertyChanged on all string properties of my viewmodel.

    protected void NotifyAllPropertiesChanged<T>()
    {
        foreach (var p in this.GetType().GetProperties())
            if (p.PropertyType == typeof(T))
                OnPropertyChanged(p.Name);
    }
Geoffrey
  • 956
  • 1
  • 10
  • 16
  • You don't understand me correct. I don't want to change resx files from .en to somethink else. I want to change values in this resx file. For example I have key who is btnCont with value Click. I want to change btnCont to be with new value Click me, for example – Evgeni Velikov Feb 15 '16 at 08:57
  • So you want to change the values in your resource file? I treat resource files as "read-only" elements. Especially since the language dll's are in the installation folder and changing them assumes that you have Administrative rights in that location (and from my experience, that is usually not the case). For "editable" strings I usually set up a database or use the Properties with a custom class that can take a default value and aliases for every culture I want to support. – Geoffrey Feb 15 '16 at 14:50
  • Not only change. One think is user can change the content in resx file. But I want and user can translate the application in their own language. – Evgeni Velikov Feb 16 '16 at 06:24
0

Try to compile following code

        <Window x:Class="WPFLocalizationSample.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>
        <StackPanel HorizontalAlignment="Left" Margin="20" VerticalAlignment="Top">
            <TextBlock Text="{DynamicResource tbName}" Margin="2" />
            <TextBox Width="200" Margin="2"/>
            <Button Content="{DynamicResource btnAdd}" HorizontalAlignment="Left" Margin="2" />
        </StackPanel>


        <StackPanel HorizontalAlignment="Left" Margin="20,181,0,0" VerticalAlignment="Top" Orientation="Vertical" Width="305">
            <TextBlock Text="Translation Panel" FontSize="20" FontWeight="Black" />
            <Grid >
                <TextBlock Text="{DynamicResource tbName}" Margin="2"  />
                <TextBox Width="200" Margin="2" HorizontalAlignment="Right" Tag="tbName"   x:Name="tbNameLoc"/>
            </Grid>
            <Grid>
                <TextBlock Text="{DynamicResource btnAdd}" Margin="2" />
                <TextBox Width="200" Margin="2" HorizontalAlignment="Right" Tag="btnAdd" x:Name="btnAddLoc" />
            </Grid>
            <Button Content="Translate/Update" HorizontalAlignment="Left" Margin="2" Click="Button_Click" />
        </StackPanel>
        </Grid>
      </Window>

Here is MainWindow.Xaml.cs

        using System;
        using System.Collections.Generic;
        using System.IO;
        using System.Windows;
        using System.Windows.Controls;
        using System.Windows.Markup;

    namespace WPFLocalizationSample
    {
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private static ResourceDictionary CurrentLanguageDictionary;
        private static string LanguageResourceFile = "en-US.xaml";
        private Dictionary<string, System.Windows.Controls.TextBox> TextBoxControls = new Dictionary<string, TextBox>();
        public MainWindow()
        {
            InitializeComponent();
            AddControlsToLocalization();
        }

        private void AddControlsToLocalization()
        {
            TextBoxControls.Add("tbName", tbNameLoc);
            TextBoxControls.Add("btnAdd", btnAddLoc);
        }

        public static bool SaveLanguageFile(Dictionary<string, System.Windows.Controls.TextBox> TextBoxControls, string selectedLang)
        {

            bool status = false;
            try
            {
                using (var fs = new FileStream(LanguageResourceFile, FileMode.Open, FileAccess.Read, FileShare.Read))
                {
                    // Read in ResourceDictionary File
                    CurrentLanguageDictionary = (ResourceDictionary)XamlReader.Load(fs);
                }
               
                foreach (string key in TextBoxControls.Keys)
                {
                    TextBox txt = TextBoxControls[key];
                    if (txt.Text.Length > 0)
                    {
                        CurrentLanguageDictionary[key] = txt.Text;
                    }
                }

                using (var writer = new StreamWriter(LanguageResourceFile))
                {
                    XamlWriter.Save(CurrentLanguageDictionary, writer);
                }
                status = true;
            }
            catch (Exception _exception)
            {
                MessageBox.Show("Exception in language file edit");
                return false;
            }

            SetLanguageResourceDictionary(LanguageResourceFile, App.Current);

            return status;

        }
        public static void SetLanguageResourceDictionary(String inFile, Application application)
        {
            if (File.Exists(inFile))
            {
                // Read in ResourceDictionary File
                var languageDictionary = new ResourceDictionary();
                languageDictionary.Source = new Uri(inFile,UriKind.Relative);

                // Remove any previous Localization dictionaries loaded
                int langDictId = -1;
                for (int i = 0; i < application.Resources.MergedDictionaries.Count; i++)
                {
                    var md = application.Resources.MergedDictionaries[i];
                    // Make sure your Localization ResourceDictionarys have the ResourceDictionaryName
                    // key and that it is set to a value starting with "Loc-".
                    if (md.Contains("ResourceDictionaryName"))
                    {
                        if (md["ResourceDictionaryName"].ToString().Equals("en-US"))
                        {
                            langDictId = i;
                            break;
                        }
                    }
                }
                if (langDictId == -1)
                {
                    // Add in newly loaded Resource Dictionary
                    application.Resources.MergedDictionaries.Add(languageDictionary);
                }
                else
                {
                    // Replace the current langage dictionary with the new one
                    application.Resources.MergedDictionaries[langDictId] = languageDictionary;
                }
            }
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            SaveLanguageFile(TextBoxControls, "en-US");
        }
    }
    }

Here is sample en-US.xaml (resource dictionary file)

    <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <sys:String x:Key="ResourceDictionaryName">en-US</sys:String>
    <sys:String x:Key="tbName">Enter youe Name</sys:String>
    <sys:String x:Key="btnAdd">Add</sys:String>
    
    </ResourceDictionary>

Adding resource file in app.xaml

    <Application x:Class="WPFLocalizationSample.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="en-US.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
    </Application>

if you compile this , you will see a form with two sample controls. below translation panel added. you can type any text that you want to change click on translate button changed text will updated in en-US file. if you want to save different file also you can do.

Note: en-US.xaml file should be in release folder.

EldHasp
  • 6,079
  • 2
  • 9
  • 24
v87
  • 27
  • 5