Giter Site home page Giter Site logo

ellumination.ngettext.wpf's Introduction

ngettext-wpf

Proper internationalization (I18N) or localization (L10N) support for WPF, facilited by NGettext inside.

Review the change history, including what's new in the latest release and what will be included in the upcomming release.

GPL

Getting Started

Find the NuGet package. You may download the package itself, or add it among your <PackageReference/> notations, and so on.

Ellumination.NGettext.Wpf is intended to work with dependency injection. Integration occurs via the following entry point from your application:

NGettext.Wpf.CompositionRoot.Compose("ExampleDomainName");

The "ExampleDomainName" string is the domain name. This means that, when the current culture is set to "da-DK", localization shall occur from Locale\da-DK\LC_MESSAGES\ExampleDomainName.mo, relative to where your WPF application is running. You must include the corresponding .mo files in your application, ensuring that they are copied to the output directory.

Now you can do something like this in XAML:

<Button CommandParameter="en-US" 
        Command="{StaticResource ChangeCultureCommand}" 
        Content="{wpf:Gettext English}" />

Which demonstrates two features of this library. First, most importantly, is the Gettext markup extension, which will make sure the Content is set to the localization of "English", with respect to current culture, and update it when the current culture has changed. Second, another feature it demonstrates is the ChangeCultureCommand, which changes the current culture to the given culture, in this case "en-US".

Have a look at Ellumination.NGettext.Wpf.Example/UpdateLocalization.ps1 for how to extract MsgId's from both .xaml and .cs files.

Note: The script will silently fail (i.e. 2> $null), initially, because there is no .po file for the given language. In the gettext world, one is supposed to create that with the msginit command, which ships with the Gettext.Tools NuGet package, or PoEdit may be used to initialize the catalog from an intermediate .pot file created.

Here is what recently worked for me:

PM> mkdir -p Locale\en-GB\LC_MESSAGES\
PM> msginit --input=obj\result.pot --output-file=Locale\en-GB\LC_MESSAGES\ExampleDomainName.po --locale=en_GB

Conventions

Keep your compiled localizations in Locale\<LOCALE>\LC_MESSAGES\<DOMAIN>.mo. This library will force you to follow this convention. Or rather, NGettext forces you to follow a convention like "<PATH_TO_LOCALES>\<LOCALE>\LC_MESSAGES\<DOMAIN>.mo", and I refined it.

Keep your raw localizations in Locale\<LOCALE>\LC_MESSAGES\<DOMAIN>.po. This is not enforced, but when working with PoEdit, it will compile the .mo file into the correct location when following this convention, and it does not remember your previous choice, so stick with the defaults.

There are lots of GNU conventions related to I18N and L10N. One of them is the notion that the original program be written in US English, so you do not need to localize anything to facilitate I18N. The original text in US English is called the msgId.

One of the most important GNU convention related to I18N is providing a context to an Interpreter so they have a chance to do it right. For instance, the English word 'order' has a number of more or less related meanings and thus may be interpreted differently depending usage. For instance, in the context of sequential ordering, 'order' translates to 'rækkefølge' in da-DK, but the imperative for placing an order translates to 'bestil'. Here is an example of how this sort of circumstance may be clarified:

<!-- A button with the text 'Order' but with a helpful context for an Interpreters -->
<Button Command="{StaticResource PlaceOrderCommand}" 
        Content="{wpf:Gettext Imperative for placing an order|Order}" />
        <!--                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -->

Interpreters will rarely think of it, and just translate the first meaning that comes to mind, and as a programmer you might not know which words or sentenses need further context. Therefore, we strongly recommend to always provide a helpful context. At the very least grammatical usage, whether noun or verb, along these lines.


Future Directions

Further directions coming soon, I just wanted to get this supporting documentation somewhat refreshed and presentable, than put my future thinking cap on for the immediate road ahead.


Support

Have a question, want to participate, you get out of it what you contribute:

  1. By participating in the project discussions;
  2. By connecting with me on Github, following my work;
  3. By contacting me on Gab, where you may reach me, primarily;
  4. By following my Twitter feed; although I do not use this for contact;
  5. By networking with me via LinkedIn, always interested in solid business leads;
  6. By asking a StackOverflow question, and by including at least the annotated tags;
  7. Or, by opening a project issue

Sample Application

As presented by Ellumination.NGettext.Wpf.Example, you will discover a sample application which illustrates the key features of the packaged dotnet assembly.

Demo

Special Thanks

Big shout out to our colleague and peer, Robert Jørgensgaard Engdahl, without whom, in whose stead we are carrying the NGettext.Wpf torch forward.

Some other mentions via StackOverflow. His former LinkedIn unavailable at the time of this entry.

I am not personally aware of his whereabouts or well being today, but where ever life may find him, in this world in the here and now, or the Great Beyond, may GOD bless and GODSPEED, my friend.

ellumination.ngettext.wpf's People

Contributors

robert-j-engdahl avatar mwpowellhtx avatar novaraine avatar pgpaccuratech avatar d-bullock avatar

Stargazers

 avatar

Watchers

 avatar  avatar

ellumination.ngettext.wpf's Issues

Consider including README markdown among NuGet package docs

In response to the following NuGet push warning:

WARNING: Readme missing. Go to https://aka.ms/nuget-include-readme learn How to include a readme file within the package

It sounds like a good idea, TBD the nature of the markdown. i.e. could run with the project markdown, or distill a separate one.

Really, however, I'd rather look at a project wiki, if I am being honest about it.

Consider default passthrough that includes context

The default passthrough is to simply treat a msgId as the thing itself you want to display. However, when that thing includes a context, for example "Menu|Tools|Options", we only want the "Options" portion in the resulting display.

Possibly can and should be contained in a default Localizer instance. Need to investigate it a bit further.

Support contextualized L10N Catalogs

The nature of this question is a bit tricky, so we will decompose the question in two parts. First, by describing how L10N catalogs are discovered today. Second, by outlining what we aim to accomplish moving forward. We will do this in two parts following the opening of the issue.

Where things may get a bit interesting is in how we rather connect the dots with culture changes, dropping the composition root approach, enhancing toward a culture changed message, along these lines. But otherwise, overall, we aim to support these elaborated use cases.

Add support for view model property annotations

Of the view model itself, again, assuming CommunityToolkit.Mvvm dependency, in particular for its ObservableObject features. Connect the dots with an appropriate binding or path as appropriate, DisplayNameAttribute annotation. Probably provided as a XAML markup extension, extending the core L10N principles, except in this area targeting this specific use case.

For example:

using System.ComponentModel;
using CommunityToolkit.Mvvm.ComponentModel;

class ViewModel : ObservableObject
{
    private int _value;

    [DisplayName("My human readable verbiage")]
    public int Value
    {
        get => _value;
        set => SetProperty(ref _value, value);
    }
}

Whereby "My human readable verbiage" is the L10N MsgId, in gettext terms.

Our design goal with this approach, is that context, especially for purposes of sourcing an appropriate ICatalog, should be entirely transparent to the view model layer of the MVVM architecture.

In terms of WPF, could perhaps scan for appropriate catalog or localizer instances among the view resources. Failing discovery of adequate view resources, fall back on a configured default. And failing defaults availability, then we allow the attribute to passthrough as is.

By accomplish this feature, views can be focused almost entirely on presentation, layout, alignment, etc. Whereas the view models are responsible almost entirely upon apporpriately conveyed content, data, messaging, etc. Saying nothing, of course, of model encapsulation.

Clean up legacy project artifacts

We can probably drop artifacts such as .nuspec, AssemblyInfo.cs files, etc. Especially now that source has been proven, is building, passing unit tests, and so forth. Just a routine sweep and triage of the code before we dive into much of anything further.

Reconsider CompositionRoot efficacy

Prefaced with the caveat, approaching the DI question, we "get it". The "composition root" is a sort of old school Castle Windsor based, IIRC, way of . Taking nothing away from that idea. But we think it can be done better "today".

As for Dependency Injection resolution, but also allowing a more effective communication when certain key events are taking place, i.e. selecting culture, for instance. We think it makes better sense to consider default implementations, but perhaps also allowing subscribers DI paths for default instances, especially considering not only catalogs but also localizers are being distributed in half a dozen other places. We especially want for the culture communication, involving the ICatalog, or an ICatalogContext (TBD) therein, to be such a natural substrate that we do not have to do too much in the way of overthinking any "roots" or "compositions".

Off the cuff, we think that perhaps it is worthwhile adopting the CommunityToolkit.Mvvm dependency supporting this aspect. Will probably go this route, even though it is more of an implementation detail. In particular its ability to relay messages, i.e. culture selection, in a weak referenced manner, very low impact, high value.

Consider default values

May consider default values use cases feeding GettextExtension usage. Not sure quite how we want to go about this just yet, but it does effect the MarkupExtension.ProvideValue(....) approach. See WPF Custom Markup Extensions for possible use cases, considerations, etc.

Provide view models code generator around annotated enumerations

Would potentially generate an EnumViewModel<T> and hosting class for glue cohesion purposes. So that such view model instances would just be there for subscribers should they require such things throughout their views. i.e.

[GettextGenerateViewModels]
enum MyEnum
{
}

And might generate something like:

class GettextMyEnumViewModel
{
    // ...
}

Along these lines.

It's a wild idea, I know. Bit beyond the scope what we are aiming to accomplish resolving couple of near medium term goals, but it has potential.

Add support for Enum value annotations

This issue addresses adding support for Enum value annotations, especially those involving DisplayAttribute annotations. The usual gettext passthrough principles should be respected. The trickier part is how we handle catalog or context discovery. For purposes of this issue, we will assume there are at least two view models which may facilitate hydration of L10N assets.

using System.Windows.Markup;

class EnumViewModel
{
    // TODO: introduce catalog or context features
    // TODO: probably as a function of CommunityToolkit.Mvvm observability, messaging recipient, etc
}

class EnumViewModel<TEnum> : EnumViewModel where TEnum : struct, Enum
{
    private readonly TEnum _value;

    public TEnum Value => _value;

    // TODO: expose L10N passthroughs of the various DisplayAttribute properties
}

The various DisplayAttribute Properties are interesting, and we can leverage them against appropriately resolved L10N resources.

Our chief design goal here is to be transparent as possible, connecting the dots between the enumerated Value, the DisplayAttribute property, and L10N assets, and get out of the way as quickly as possible.

Use cases for this approach can include things like, displaying specific enumerated values, presenting comboboxes, various tooltip use cases, such like this.

As a proof of concept, we've already proven that we can wrap the Enum value and DisplayAttribute aspects. What remains is to also resolve in terms of NGettext ICatalog.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.