0

I have successfully create a multilanguage application based from here. I was great when able to change language.

Now I have stuck in this situation. My application is operating with hardware. So there is one screen that having interaction with hardware and display status textblock. The message will be variant depend on the response from hardware e.g. "Please wait..", "Scan your ID into scanner", "Scan complete", "Profile identified, continue with transaction".

How do this variant can be display in multilingual into single textblock?

Assuming the textblock will be naming TbxStatus.Text. How do I set the message in ResourceDictionary file and how do I handle which resource string key that it should take?

EDITED [WHAT HAVE I TRIED]

This is the code that I've write to switch language and show based from resources dictionary:-

App.cs

public static String Directory;
public static App Instance;
public static event EventHandler LanguageChangedEvent;
public App()
{
    // Initialize static variables
    Instance = this;
    Directory = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
    Instance.SetLanguageResourceDictionary(Instance.GetLocXAMLFilePath("en-US"));
}

public static void LoadLanguageLocalization()
{
    try
    {
        ViewModel.AppConfigViewModel.LocalizationProperty.LangLoc = new List<ApplicationModel.LanguageLocalization>
        {
            new ApplicationModel.LanguageLocalization { LanguageID = 1, CountryCode = "ms-MY", LanguageName = "Bahasa Malaysia" },
            new ApplicationModel.LanguageLocalization { LanguageID = 2, CountryCode = "en-US", LanguageName = "English" },
            new ApplicationModel.LanguageLocalization { LanguageID = 3, CountryCode = "zh-CN", LanguageName = "Chinese" },
            new ApplicationModel.LanguageLocalization { LanguageID = 4, CountryCode = "ta-IN", LanguageName = "Tamil" }
        };
    }
    catch (Exception ex)
    {
        LogEvents($"[App] Exception on LoadLanguageLocalization. Message-{ex.Message}. Stack Trace-{ex.StackTrace}", EventLogEntryType.Error);
        ViewModel.AppConfigViewModel.LocalizationProperty.LangLoc = null;
    }
}
public void SwitchLanguage(string inFiveCharLang)
{
    if (System.Globalization.CultureInfo.CurrentCulture.Name.Equals(inFiveCharLang))
        return;

    var ci = new System.Globalization.CultureInfo(inFiveCharLang);
    Thread.CurrentThread.CurrentCulture = ci;
    Thread.CurrentThread.CurrentUICulture = ci;

    SetLanguageResourceDictionary(GetLocXAMLFilePath(inFiveCharLang));
    LanguageChangedEvent?.Invoke(this, new EventArgs());
}
private string GetLocXAMLFilePath(string inFiveCharLang)
{
    string locXamlFile = "Resources." + inFiveCharLang + ".xaml";
    return Path.Combine(Directory, "Language", locXamlFile);
}
public void SetLanguageResourceDictionary(String inFile)
{
    if (File.Exists(inFile))
    {
        // Read in ResourceDictionary File
        var languageDictionary = new ResourceDictionary();
        languageDictionary.Source = new Uri(inFile);

        // Remove any previous Localization dictionaries loaded
        int langDictId = -1;
        for (int i = 0; i < Resources.MergedDictionaries.Count; i++)
        {
            var md = 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("LanguageDictionaryName"))
            {
                if (md["LanguageDictionaryName"].ToString().StartsWith("Lang-"))
                {
                    langDictId = i;
                    break;
                }
            }
        }
        if (langDictId == -1)
        {
            // Add in newly loaded Resource Dictionary
            Resources.MergedDictionaries.Add(languageDictionary);
        }
        else
        {
            // Replace the current langage dictionary with the new one
            Resources.MergedDictionaries[langDictId] = languageDictionary;
        }
    }
}

SelectLanguage.cs

private async void Page_Loaded(object sender, RoutedEventArgs e)
{
    try
    {
        App.LogEvents($"[{PageTitle}] Loaded: Select language", System.Diagnostics.EventLogEntryType.Information);
        BindingToPropertyControl();
    }
    catch (System.Exception ex)
    {
        string error = $"[{PageTitle}] Exception on Page_Loaded. Message: {ex.Message}. StackTrace: {ex.StackTrace}";
        App.LogEvents(error, System.Diagnostics.EventLogEntryType.Error);
    }
}
private void BindingToPropertyControl()
{
    try
    {
        if (ViewModel.AppConfigViewModel.LocalizationProperty.LangLoc != null)
        {
            LanguagePack.ItemsSource = ViewModel.AppConfigViewModel.LocalizationProperty.LangLoc;
        }
    }
    catch (System.Exception ex)
    {
        string error = $"[{PageTitle}] Exception on BindingToPropertyControl. Message: {ex.Message}. StackTrace: {ex.StackTrace}";
        App.LogEvents(error, System.Diagnostics.EventLogEntryType.Error);
    }
}
private void Button_Click(object sender, RoutedEventArgs e)
{
    try
    {
        ScreenTimer.Stop();
        Button btn = (Button)sender;
        string LangCode = btn.Tag.ToString();
        App.LogEvents($"[{PageTitle}] Selecting language: {LangCode}", System.Diagnostics.EventLogEntryType.Information);
        App.Instance.SwitchLanguage(LangCode.ToString());
        Dispatcher.BeginInvoke(DispatcherPriority.Send, new Action(delegate ()
        {
            NavigationService.Navigate(new Uri(ApplicationModel.NaviModel.NaviSelectOptions, UriKind.RelativeOrAbsolute));
        }));
    }
    catch (System.Exception ex)
    {
        string error = $"[{PageTitle}] Exception on Button_Click. Message: {ex.Message}. StackTrace: {ex.StackTrace}";
        App.LogEvents(error, System.Diagnostics.EventLogEntryType.Error);
    }
}

SelectLanguage.xaml

<ScrollViewer x:Name="ScrollLanguage" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden">
    <WrapPanel Height="Auto" Width="{Binding ElementName=ScrollLanguage, Path=ViewportWidth}">
        <ItemsControl Name="LanguagePack">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Grid Margin="0,20" VerticalAlignment="Stretch" Width="{Binding ElementName=ScrollLanguage, Path=ViewportWidth}">
                        <Button Click="Button_Click" Tag="{Binding CountryCode}" Content="{Binding LanguageName}" VerticalAlignment="Center" Height="150" FontSize="60" Background="#FF1A5C9E" BorderBrush="{x:Null}"/>
                    </Grid>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </WrapPanel>
</ScrollViewer>

SelectOptions.xaml

<TextBlock x:Name="tbTitle" TextWrapping="Wrap" Text="{StaticResource ResourceKey=SelectMerchant_Title}" FontSize="100" TextAlignment="Center" Padding="0,0,0,50" Foreground="White"/>
<Button x:Name="btnEatIn" Content="{StaticResource ResourceKey=SelectMerchant_Opt1}" VerticalAlignment="Center" Height="150" FontSize="60" Background="#FF057A5A" BorderBrush="{x:Null}"/>
<Button x:Name="btnEatIn" Content="{StaticResource ResourceKey=SelectMerchant_Opt2}" VerticalAlignment="Center" Height="150" FontSize="60" Background="#FF057A5A" BorderBrush="{x:Null}"/>

Resources.en-US.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib">

    <!-- The name of this ResourceDictionary. Should not be localized. -->
    <sys:String x:Key="LanguageDictionaryName" Localization.Comments="$Content(DoNotLocalize)">Lang-en-US</sys:String>

    <!-- Localization specific styles -->
    <FlowDirection x:Key="FlowDirection_Default" Localization.Comments="$Content(DoNotLocalize)">LeftToRight</FlowDirection>
    <!--<FlowDirection x:Key="FlowDirection_Reverse" Localization.Comments="$Content(DoNotLocalize)">RightToLeft</FlowDirection>-->

    <!-- SELECT ORDER TYPE -->
    <sys:String x:Key="SelectMerchant_Title">Self-Service Kiosk</sys:String>
    <sys:String x:Key="SelectMerchant_Opt1">Register new applicant</sys:String>
    <sys:String x:Key="SelectMerchant_Opt2">Meal Application</sys:String>
</ResourceDictionary>

Back to what I'm facing, I can show different language by using resource key, but how to I display message or status which is dynamically (not static) into the display in multi-language?

Example, on validation screen, I have one TextBlock and currently I'm subscribe the event raise from hardware. How to show the status based from language that has been selected? .

<StackPanel VerticalAlignment="Top" Margin="120,180,120,0" Grid.Row="1">
       <TextBlock x:Name="tbGeneralStatus" TextWrapping="Wrap" Text="Please wait..." TextAlignment="Center" FontSize="50" Foreground="Yellow"/>
</StackPanel>

tbGeneralStatus.Text will show "Please wait..", "Scan your ID into scanner", "Scan complete", "Profile identified, continue with transaction" from delegate event from Barcode Scanner class.

Luiey
  • 843
  • 2
  • 23
  • 50
  • Post a bit of code to show us what you have tried in WPF so far – mahlatse Nov 23 '18 at 07:27
  • Possible duplicate of [How to use localization in C#](https://stackoverflow.com/questions/1142802/how-to-use-localization-in-c-sharp) – LittleBit Nov 23 '18 at 07:27
  • @LittleBit this is not duplicate question. I'm able to change different language based from the link that I follow, and my problem is not switching the screen language based from selection, but handling multiple language into single text such as "Please wait..", "Scan your ID into scanner", "Scan complete", "Profile identified, continue with transaction". I have searching for this before I'm asking into this forum – Luiey Nov 23 '18 at 07:37
  • Probably you already have a enum for your hardware dependant statuses. In such case, you need to decide how to localize your enums. Find different strategies here: https://stackoverflow.com/questions/17380900/enum-localization – zameb Nov 23 '18 at 08:03
  • @mahlatse updated my posting – Luiey Nov 23 '18 at 08:14
  • @zameb I can't find which solution is related to my question, can you assist to point which one? – Luiey Nov 23 '18 at 08:16
  • Question is not related to https://stackoverflow.com/questions/1142802/how-to-use-localization-in-c-sharp – Luiey Nov 23 '18 at 08:17
  • I highly suggest using the [WPF Localization Extension](https://github.com/XAMLMarkupExtensions/WPFLocalizationExtension) instead of doing it yourself. – Shahin Dohan Nov 23 '18 at 10:52

1 Answers1

0

I think you need to look a little into MVVM to make things easier with WPF. It takes some effort at the beggining, but its absolutely worth. I thought you were stucked only on how to receive and translate the status, so I'll try to give more info after looking at the code.

A quick guidance, based on Enum localization but not verified in dept.

You need a viewmodel to act as datacontext of the window you want to update. It has to implement INotifyPropertyChange interface to update the status in live.

class YourWindowViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    private string _statusText = string.Empty;
    public string StatusText
    {
        get { return _statusText; }
        set
        {
            _statusText = value;
            OnPropertyChanged("StatusText");
        }
    }

    public void YourMessageHandler(Status newStatus)
    {
        StatusText = GetLocalizedStatusText(newStatus);
    }

    private string GetLocalizedStatusText(Status newStatus)
    {
        switch (newStatus)
        {
            case Status.Wait: return Resources.StatusWaiting;
            case Status.Continue: return Resources.StatusContinue;
            case Status.Scan: return Resources.StatusScanId;
            default: return string.Empty;
        }
    }
}

public enum Status
{
    Wait,
    Scan,
    Continue
}

To bind to your window, make it like this:

<Window.DataContext>
    <local:YourWindowViewModel/>
</Window.DataContext>

and change your TextBlock control to bind to the StatusText on the viewmodel

<StackPanel VerticalAlignment="Top" Margin="120,180,120,0" Grid.Row="1">
    <TextBlock TextWrapping="Wrap" Text="{Binding StatusText}" TextAlignment="Center" FontSize="50" Foreground="Yellow"/>
</StackPanel>

Note that as I don't know your delegate/msgHandler format, I have put a generic "YourMessageHandler" method which receive the changing status

zameb
  • 705
  • 11
  • 26
  • To be honest, I'm not understanding and get the idea how to apply the MVVM pattern into my current application. I have go through various MVVM tutorial, but I'm lost how it can be done in my application specifically how to control the wpf page(view) from other file(viewmodel) at which curently I'm handling the screen behind the page itself, not from other class (traditional approach). – Luiey Nov 26 '18 at 01:17