views:

85

answers:

4

Are there any good practices to follow when designing a model/ViewModel to represent data in an app that will view/edit that data in multiple languages? Our top-level class--let's call it Course--contains several collection properties, say Books and TopicsCovered, which each might have a collection property among its data. What kind of class structure should I implement to hold the data for certain properties in multiple languages?

For example, the data needs to represent course1.Books.First().Title in different languages, and course1.TopicsCovered.First().Name in different languages. We want a app that can edit any of the data for one given course in any of the available languages--as well as edit non-language-specific data, perhaps the Author of a Book (i.e. course1.Books.First().Author). We are having trouble figuring out how best to set up the model to enable binding in the XAML view.

For example, do we replace (in the single-language model) each String with a collection of LanguageSpecificString instances? So to get the Title in the current language:

course1.Books.First().Title.Where(lss => lss.Language==CurrentLanguage).SingleOrDefault()

If we do that, we cannot easily bind to any value in one given language, only to the collection of language values such as in an ItemsControl.

<TextBox Text={Binding Title.???} />  <!-- no way to bind to the current language title -->

Do we replace the top-level Course class with a collection of language-specific Courses? So to get the title in the current language:

course1.GetLanguage(CurrentLanguage).Books.First().Title

If we do that, we can only easily work with one language at a time; we might want a view to show one language and let the user edit the other.

<TextBox Text={Binding Title} />  <!-- good -->
<TextBlock Text={Binding ??? } />  <!-- no way to bind to the other language's title-->

Also, that has the disadvantage of not representing language-neutral data as such; every property (such as Author) would seem to be in multiple languages. Even non-string properties would be in multiple languages.

Is there an option in between those two? Is there another way that we aren't thinking of?

I realize this is somewhat vague, but it would seem to be a somewhat common problem to design for.

Note: This is not a question about providing a multilingual UI, but rather about actually editing multi-language data in a flexible way.

Edit: so the question is how should we model the classes? Something like this:

Class Course { 
    ObservableCollection<Book> Books
    ObservableCollection<Topic> TopicsCovered
}
Class Book {
    ObservableCollection<LanguageSpecificString> Title
    String Author
}
Class Topic {
    ObservableCollection<LanguageSpecificString> Name
}
Class LanguageSpecificString {
    String LanguageCode
    String Value
}

Or should it be something like:

Class MultilingualCourse { 
    Course GetLanguage(string languageCode)
}
Class Course { 
    ObservableCollection<Book> Books
    ObservableCollection<Topic> TopicsCovered
}
Class Book {
    String Title
    String Author
}
Class Topic {
    String Name
}

Or something else altogether?

A: 

If I'm understanding you correctly you want to localize data and also the presentation of the data? IE when listing Books, the column "Author" will be in the chosen language? For the UI localization we implemented something along the lines of this. http://www.wpftutorial.net/LocalizeMarkupExtension.html For localizing data, I think you'd have to have a column specifying what locale you are querying. English data will be separate from French data, etc.

I don't understand your binding issue. The Binding name shouldn't change. Nor should your model properties. If you have a property of "Author" it will always stay that. If you want to display that on screen in another language then you use the way listed above or some other one to localize displayable strings. Same goes for messages. That way a user can pick their language and you display everything in their language.

nportelli
For clarity, I changed this to Title (not Author). Yes, we have Title stored in two different languages--the question is how to structure it to most easily bind to one or the other or both in a given view? There will be some views that show both languages.
Patrick Szalapski
I assumed you would only display one language at a time. I think I understand what you are asking. I think your model would return a list of a requested field of all languages. Then display the list(observableCollection). Still your binding variable doesn't need to be changed. It shouldn't be changed for the very reason you are asking the question. How to bind?
nportelli
+1  A: 

How about this (I've omitted change notification in my example):

public class BookViewModel
{
    private readonly Book book;
    private readonly CultureInfo viewCulture;

    // constructor
    public BookViewModel(Book book, CultureInfo viewCulture)
    {
        this.book = book;
        this.viewCulture = viewCulture;
    }

    public string OriginalTitle
    {
        get { return this.book.OriginalTitle; }
        set { this.book.OriginalTitle = value; }
    }

    public string TranslatedTitle
    {
        get { return this.book.GetCultureSpecificTitle(this.viewCulture); }
        set { this.book.SetCultureSpecificTitle(this.viewCulture, value); }
    }

    public string Author
    {
        get { return this.book.Author; }
        set { this.book.Author = value; }
    }
}

You also mention needing to construct views were multiple languages are involved. I assumed here that you are building a translation interface, and you only need to show the original language and the translation target language.

But if you need to be able to show any two languages side by side, you could still build a viewmodel with a viewCulture1 and viewCulture2, and corresponding exposed properties like Title1 and Title2. Or alternatively, you could build composite views where different parts of the view have viewmodels with different associated languages (cf. the use of nested viewmodels and data templates in Josh Smith's example).

You could also expose the view culture as a mutable property, and bind a combo box to it to dynamically let the user switch the view between languages, etcetera.

For the model side I have used GetCultureSpecificX and SetCultureSpecificX methods, but alternative mechanisms (like your IEnumerable<LanguageSpecificString> suggestion) could also be adapted to a simple string property in the viewmodel.

Wim Coenen
Your example hasn't shown me where GetCultureSpecificTitle() gets its data from. Suppose I implement your example, and then I bind to TranslatedTitle. Meanwhile, something else in the view model changes the translated value. The UI will not know that TranslatedTitle has changed, for nothing has notified property change on that.
Patrick Szalapski
@Patrick: note that I start my answer with "I've omitted change notification in my example". In any case, the way you implement change notification is not specific to the language issue. You just need change events in the model, which are translated into PropertyChanged events in the viewmodel. Isn't that central to the MVVM pattern itself? Also, I can't make decisions about the source of the language data for you. It could be a file. It could be a webservice. It could be a database. That's really up to you, as I don't know the rest of your requirements.
Wim Coenen
A: 

Instead of monkeying around with your binding and your class design, consider monkeying around with DataTemplateSelectors. If you have a locale-aware string class, for instance, that you want to display in a TextBox if it's in the current language and in a TextBlock if it's not, you can put that decision point in a template selector. Every instance of, say,

<ContentControl Content="{Binding LocaleAwareStringProperty}"
                ContentTemplateSelector="{StaticResource StringTemplateSelector}"/>

will get rendered with the proper control. (Of course you can set the ContentTemplateSelector in a style, which would simplify the XAML a bunch.)

Robert Rossney
A: 

We've decided to go simpler than these answers and just replace each string that is translatable with a Dictionary<string, string> (language, value). Of course, we may wish to Dictionary<CultureInfo, string> in the future. This is close to my first answer in the original post.

Patrick Szalapski