Dynamic translation of the annex in C#



  • How to make a dynamic translation of the annex to WPF C# so that the language from Russian to English and from Russian can be changed directly in the annex. What's the best way to do that? Ideally refer to github projects on localized applications. I'll take care of it.



  • I was doing the next thing.

    1. All the localized lines put in ResourceDictionary♪ The XAML references to them are made through DynamicResource
    2. Sam. ResourceDictionary in satellite assembly.
    3. Satellite assembly for other languages is established through the Maycroft LocBaml https://msdn.microsoft.com/en-us/library/ms771568%28v=vs.90%29.aspx ♪ http://www.codeproject.com/Articles/17334/Localizing-WPF-Applications-using-Locbaml )
    4. That's enough for the right localization when the application starts. The following trick is needed to localize for the summer:
      • change CurrentCulture and CurrentUICulture UI flow (if several, each).
      • Read all the localizations. ResourceDictionaryfor this, we remove them from where they were connected, and we add them again. (or otherwise, so there's no time when there's no localization.)

    The problem that must be addressed is composite lines (e.g.: You can use free version {0} till activation is neededwhere {0} Open 2 days or 3 weeks) They had to write their own bike of this kind:

    [ContentProperty("Arguments")]
    [DefaultProperty("Value")]
    public class StringFormat : InvisibleHelperElement
    {
        static List<DependencyProperty> InlineArgProperties;
    
    static StringFormat()
    {
        InlineArgProperties = new List&lt;DependencyProperty&gt;()
            { Arg0Property, Arg1Property, /*9 штук*/ };
        // на самом деле тут страшновато выглядящий код с рефлексией
    }
    
    public StringFormat()
    {
        Arguments = new ObservableCollection&lt;FormatArgument&gt;();
        Arguments.CollectionChanged += OnCollectionChanged;
    }
    
    public string Value
    {
        get { return (string)GetValue(ValueProperty); }
        set { SetValue(ValueProperty, value); }
    }
    
    public static readonly DependencyProperty ValueProperty =
        DependencyProperty.Register("Value", typeof(string), typeof(StringFormat));
    
    public object Arg0
    {
        get { return GetValue(Arg0Property); }
        set { SetValue(Arg0Property, value); }
    }
    
    public static readonly DependencyProperty Arg0Property =
        DependencyProperty.Register("Arg0", typeof(object), typeof(StringFormat),
            new UIPropertyMetadata(FormatConstants.UnsetArgValue, UpdateValue));
    
    // тут ещё такие же Arg1, ... Arg9
    
    public object FallbackValue
    {
        get { return GetValue(FallbackValueProperty); }
        set { SetValue(FallbackValueProperty, value); }
    }
    
    public static readonly DependencyProperty FallbackValueProperty =
        DependencyProperty.Register("FallbackValue", typeof(object), typeof(StringFormat),
            new UIPropertyMetadata(FormatConstants.UnsetArgValue, UpdateValue));
    
    public ObservableCollection&lt;FormatArgument&gt; Arguments { get; private set; }
    
    // Нужно переопределить этот метод, чтобы аргументы воспринимались как
    // логические дочерние элементы
    protected override System.Collections.IEnumerator LogicalChildren
    {
        get { return Arguments.GetEnumerator(); }
    }
    
    public string Format
    {
        get { return (string)GetValue(FormatProperty); }
        set { SetValue(FormatProperty, value); }
    }
    
    public static readonly DependencyProperty FormatProperty =
        DependencyProperty.Register(
                "Format", typeof(string), typeof(StringFormat),
                new FrameworkPropertyMetadata(UpdateValue));
    
    // этот callback будет вызываться, когда список аргументов обновится.
    // добавляет/убирает аргументы в визуальное дерево, подписывается/отписывается
    // от их изменений
    void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.NewItems != null)
            foreach (object item in e.NewItems)
            {
                var formatArg = item as FormatArgument;
                if (formatArg == null)
                    continue;
                AddLogicalChild(formatArg);
                AddVisualChild(formatArg);
                formatArg.PropertyChanged += OnArgumentChanged;
            }
    
        if (e.OldItems != null)
            foreach (object item in e.OldItems)
            {
                var formatArg = item as FormatArgument;
                if (formatArg == null)
                    continue;
                formatArg.PropertyChanged -= OnArgumentChanged;
                RemoveVisualChild(formatArg);
                RemoveLogicalChild(formatArg);
            }
    
        UpdateValue();
    }
    
    // этот callback вызывается когда изменяется какой-либо из аргументов.
    // обновляет результат
    void OnArgumentChanged(object sender, EventArgs e)
    {
        UpdateValue();
    }
    
    object[] GatherParams()
    {
        var inlineArgs =
            InlineArgProperties.Select(p =&gt; GetValue(p))
                               .TakeWhile(v =&gt; v != FormatConstants.UnsetArgValue)
                               .ToList();
        var contentArgs = Arguments.Select(arg =&gt; arg.Value).ToList();
        return inlineArgs.Concat(contentArgs).ToArray();
    }
    
    void UpdateValue()
    {
        if (Format != null)
        {
            try
            {
                Value = string.Format(CultureInfo.CurrentCulture, Format, GatherParams());
                return;
            }
            catch (FormatException)
            {
            }
        }
        Value = (FallbackValue != FormatConstants.UnsetArgValue &amp;&amp; FallbackValue != null) ?
                    FallbackValue.ToString() : null;
    }
    

    }

    public class FormatArgument : InvisibleHelperElement, INotifyPropertyChanged
    {
    public object Value
    {
    get { return GetValue(ValueProperty); }
    set { SetValue(ValueProperty, value); }
    }

    public static readonly DependencyProperty ValueProperty =
        DependencyProperty.Register(
            "Value", typeof(object), typeof(FormatArgument),
            new PropertyMetadata(
                FormatConstants.UnsetArgValue,
                (o, args) =&gt; ((FormatArgument)o).NotifyPropertyChanged()));
    
    public event PropertyChangedEventHandler PropertyChanged;
    
    void NotifyPropertyChanged()
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(ValueProperty.Name));
    }
    

    }

    public class InvisibleHelperElement : FrameworkElement
    {
    static InvisibleHelperElement()
    {
    WidthProperty.OverrideMetadata(
    typeof(InvisibleHelperElement), new FrameworkPropertyMetadata(0.0));
    HeightProperty.OverrideMetadata(
    typeof(InvisibleHelperElement), new FrameworkPropertyMetadata(0.0));
    FocusableProperty.OverrideMetadata(
    typeof(InvisibleHelperElement), new FrameworkPropertyMetadata(false));
    }
    }

    static class FormatConstants
    {
    static public readonly object UnsetArgValue = new object();
    }

    Use this:

    <h:StringFormat Name="LicenseUpdatePromptText"
    Format="{DynamicResource ID_DEAR_USERNAME_UPDATE_YOUR_WHATEVER}"
    Arg0="{Binding Username}"
    Arg1="{DynamicResource ID_ACTIVATION_KEY}"/>

    <TextBlock Text="{Binding Value, ElementName=LicenseUpdatePromptText}"/>

    Another problem is how to transfer a composite localized line from VM to View, which also had to be written by a rather extensive bicycle.




Suggested Topics

  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2