robert-j-engdahl / ngettext-wpf

Proper internationalization support for WPF (via NGettext)
GNU Lesser General Public License v3.0
10 stars 12 forks source link

Does this support multibindings? #47

Open D-Bullock opened 4 years ago

D-Bullock commented 4 years ago

I'm looking to try and do something like this


<TextBlock >
    <TextBlock.Text>
        <MultiBinding Converter="{wpf:GettextFormatConverter {}{0} {1} some text to translate}">
            <Binding Path="path1"  />
            <Binding Path="path2" />
            <Binding />
        </MultiBinding>
    </TextBlock.Text>
</TextBlock>````

Is it possible? 
mwpowellhtx commented 6 months ago

Funny that, "not yet", but I may be onto something with regard to such. For a couple of reasons.

In this use case, obviously, whereby path1 and path2 are desired as part of the format.

But also as a means of exposing either the nearest Parent FrameworkElement to the converter, or better yet, just connect the dots with a <LocalizationContent/> area.

So, something like this:

<TextBlock>
    <TextBlock.Text>
        <MultiBinding Converter="{wpf:GettextMultiFormatConverter {}{0} {1} some text to translate}">
            <Binding Path="path1"  />
            <Binding Path="path2" />
            <Binding Source="{RelativeSource Mode=FindAncestor, AncestorType=TextBlock}" />
        </MultiBinding>
    </TextBlock.Text>
</TextBlock>

Or when L10N isolation is desired:

<LocalizationContent DomainName="MyDomain">
    <TextBlock HorizontalAlignment="Center">
        <TextBlock.Text>
            <MultiBinding Converter="{wpf:GettextMultiFormatConverter Binding string format support: {0:n0}}">
                <Binding Path="Counter" Mode="OneWay" />
                <Binding Source="{RelativeSource Mode=FindAncestor, AncestorType=LocalizationContent}" />
            </MultiBinding>
        </TextBlock.Text>
    </TextBlock>
</LocalizationContent>

The challenge is that we really need the root FrameworkElement for which the Converter is serving. However, when we encounter MarkupExtension.ProvideValue and make the appropriate TargetObject decisions, that instance is instead the Binding, or would be MultiBinding, in this case, itself. So for that reason, MultiBinding has some obvious benefits, being able to workaround that limitation.

When dealing in the non-Converter L10N scenarios, we see TargetObject as the DependencyObject, literally the root FrameworkElement instance. In either case, once we have said parent, we can then easily track back to a LocalizationContent area, is possible, and negotiate DomainName accordingly. So however we got there, from TargetObject, or a multi-binding, that's the rub. Versus TargetObject being the Binding instance itself; that being the case, short of routed attached events, I'm not sure how else to connect the dots, but the events thing has limited value, so not especially interested to add that overhead for what it is.

The tricky part, is then what do we do with the object[] values during the conversion itself. In other words, factoring becomes a little bit different, translating, pardoning the pun, in the Convert method instead of the ProvideValue method, for purposes of arresting the core L10N request, along these lines:

public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    => Localizer?.Gettext(MsgId, values) ?? MsgId;

I'm leaning towards separating out any of the LocalizationContent, or potentially also ILocalizationDomain with support for an ILocalizationDomain.DomainName property, or otherwise FrameworkElement values, and passing the values except those instances along to the subscriber Gettext L10N format.

Okay, so in summary, let's say this approach has potential, there is a migration path towards IMultiValueConverter from IValueConverter. In and of itself, that can be clean, or a breaking change, considering support also for the ILocalizationDomain.DomainName isolation. Of course the naive approach could simply add the DomainName property on the IValueConverter itself. I do not like that XAML curb appeal, to be honest, but I do not know how else to work around the limits.

Your thoughts? 🧠 🍻

D-Bullock commented 6 months ago

You are so far beyond where I normally operate I can give no opinions. I am sorry!

mwpowellhtx commented 6 months ago

Fair enough my friend. Thank you.

mwpowellhtx commented 6 months ago

I was able to support such a use case in the project.

<TextBlock.Text>
    <MultiBinding Converter="{wpf:GettextMultiFormatConverter Binding string format support: {0:n0}\, even or odd: {1}}">
        <Binding Path="Counter" Mode="OneWay" />
        <Binding Path="EvenOrOdd" Mode="OneWay" />
        <Binding Source="{RelativeSource Mode=FindAncestor, AncestorType=TextBlock}" />
    </MultiBinding>
</TextBlock.Text>

Which also afforded a change to work with the .po files, as well as an MSBuild <Target/> area building those to .mo files.