How to create a multilingual domain model

I use domain design, and I have a pretty clear idea of ​​my domain model. It contains over 120 classes and is quite stable. We implement it in .NET 4 and C #. The fact is that the model must be multilingual; some attributes must be stored in several languages. For example, the Person class has a Position property of type string , which should store a value in English (for example, "Librarian") and Spanish (for example, "Bibliotecario"). The getter for this property should return the English or Spanish version depending on some language parameter.

So start my questions. I am not sure how to parameterize this. I have identified two main ways to do this:

  • Use property collections. The position will not be string , but rather a Dictionary<Language, string> , which will allow customers to get a person’s position in the language.
  • Keep simple, scalar properties, but force them to return (or set) a value for one or another language, depending on the public setting of the current language. Client code would set the working language, and then all objects would install and get values ​​in that language.

Option 1 avoids the global state, but it interferes with the interface of almost every class in my model. On the other hand, option 2 is less expressive, since you cannot say what language you are going to get without looking at the global settings. In addition, it introduces a dependency in each class in the global setting.

Note that I'm not interested in database implementations or ORMs; I work only at the domain model level.

I have two specific questions:

  • What is the best option (1 or 2) to achieve my goal of a multilingual domain model?
  • Are there any other options that I have not considered, and what are they?

Thanks.

Edit Some of them suggested that this is a problem with the user interface, and therefore it can be solved with the support of globalization / localization in .NET. I do not agree. Localization of the user interface only works if you know the localized literals that should be displayed to the user at compile time, but this is not our case. My question includes multilingual data that is unknown at compile time, as it will be provided as user data at runtime. This is not a user interface problem.

Change 2 . Please keep in mind that Person.Position is just a toy example illustrating the question. This is not part of the real model. Do not try to criticize him or improve him; that makes no sense. Our business requirements include many attributes that cannot be encoded as enumeration types or similar, and should remain free text. Hence the difficulty.

+4
source share
7 answers

Given the following:

In some use cases, it is necessary to set values ​​for an object in all supported languages; others suggest viewing values ​​in one given language.

I would suggest going for both options. This means that Person and all classes containing multilingual content must keep this content in their state and:

  • The Position property should set / get the person’s position in the current one .

  • For all , there must be an appropriate property or method, language setting / getting.

  • There should be a method to install (or even switch if necessary) the user's language. I would create an abstract class (e.g. BaseMultilingualEntity) with an abstract SetLanguage (lang Language) as well as GetOne. You need to keep track of all the objects that come from BaseMultilingualEntity in some registry that will expose the language settings.

CHANGE WITH SOME CODE

 public enum Language { English, German } // all multilingual entity classes should derive from this one; this is practically a partly implemented observer public abstract class BaseMultilingualEntity { public Language CurrentLanguage { get; private set; } public void SetCurrentLanguage(Language lang) { this.CurrentLanguage = lang; } } // this is practically an observable and perhaps SRP is not fully respected here but you got the point i think public class UserSettings { private List<BaseMultilingualEntity> _multilingualEntities; public void SetCurrentLanguage(Language lang) { if (_multilingualEntities == null) return; foreach (BaseMultilingualEntity multiLingualEntity in _multilingualEntities) multiLingualEntity.SetCurrentLanguage(lang); } public void TrackMultilingualEntity(BaseMultilingualEntity multiLingualEntity) { if (_multilingualEntities == null) _multilingualEntities = new List<BaseMultilingualEntity>(); _multilingualEntities.Add(multiLingualEntity); } } // the Person entity class is a multilingual entity; the intention is to keep the XXXX with the XXXXInAllLanguages property in sync public class Person : BaseMultilingualEntity { public string Position { set { _PositionInAllLanguages[this.CurrentLanguage] = value; } get { return _PositionInAllLanguages[this.CurrentLanguage]; } } private Dictionary<Language, string> _PositionInAllLanguages; public Dictionary<Language, string> PositionInAllLanguages { get { return _PositionInAllLanguages; } set { _PositionInAllLanguages = value; } } } public class Program { public static void Main() { UserSettings us = new UserSettings(); us.SetCurrentLanguage(Language.English); Person person1 = new Person(); us.TrackMultilingualEntity(person1); // use case: set position in all languages person1.PositionInAllLanguages = new Dictionary<Language, string> { { Language.English, "Software Developer" }, { Language.German, "Software Entwikcler" } }; // use case: display a person position in the user language Console.WriteLine(person1.Position); // use case: switch language us.SetCurrentLanguage(Language.German); Console.WriteLine(person1.Position); // use case: set position in the current user language person1.Position = "Software Entwickler"; // use case: display a person position in all languages foreach (Language lang in person1.PositionInAllLanguages.Keys) Console.WriteLine(person1.PositionInAllLanguages[lang]); Console.ReadKey(); } } 

code>

+5
source

A domain model is an abstraction - it models a certain part of the world, it captures the concept of a domain.

A model exists, so developers can communicate in code as domain experts report, using the same names for the same concepts.

Now the Spanish expert and the English expert can use different words for the same concept, but the concept itself will be the same (hopefully, although the language may be ambiguous, and people do not always understand the same concept in the same way, but I got distracted).

The code must choose one human language for these concepts and stick to it. There is absolutely no reason for the model to consist of different languages ​​to represent a single concept.

Now you may need to show users application data and metadata in their language, but the concept does not change.

In this regard, you are the second option - this is what you should do - with .NET, this is usually done by looking at CurrentThread.CurrentCulture and / or CurrentThread.CurrentUICulture and using satellite assemblies that will contain localized resources.

+3
source

My question includes multilingual data

[...]

Note that I am not interested in a database or an ORM implementation;

In these two statements, I see some contradiction. Whatever the final decision, in any case, you will have multilingual structures in your database, as well as a mechanism that requests them for translation, right?

The fact is that if your domain is really not connected with the translation, I will try to keep it from multilingual problems as much as possible, for the same reason that you would try to make your uncertainty about the domain uninformed or UI uninformed.

Thus, I would at least put the logic of multilingual resolution at the infrastructure level. Then you could use aspects to give multilingual behavior to some properties if you really need to trace multiple languages ​​in your entities and don’t want your persistence level to handle all this transparently:

 public class Person { [Multilingual] public string Position { get; set; } } 
+1
source

It contains more than 120 classes and is quite stable.

It is not directly related to the issue, but you might think of the existence of many limited contexts in your domain.

I agree with Oded that it seems that in your script, language is a user interface problem. Of course, a domain can be declared through a combination of C # and English; what it represents is abstract. The user interface will handle the CultureInfo.CurrentCulture language - effective option # 2.

The Person object with the Position property does not define the natural language used to represent the position. You may have a use case where you want to display a position in one language while it is initially stored in another. In this case, you may have a translator as part of the user interface. It is like representing money as a pair of sum and currency, and then converting between currencies.

EDIT

The recipient for this property must return the English or Spanish version, depending on some language parameter.

What determines this language setting? What is responsible for the fact that, for example, Position is stored in several languages? Or is the translation done on the fly? Who is the client of the property? If a client defines a language parameter, why can't a client translate without involving a domain? Is there a multi-language behavior, or is it just a read issue? A DDD point is to redistribute your primary behavioral domain and change aspects related to the data request to other areas of responsibility. For example, you can use an image model template to access the Position property of an aggregate with a specific language.

0
source

Make the user explicit!
I have already come across domains where the user culture is a first-class citizen in the domain, but in such situations I model the correct value object (in your example, I would use the Position class class that implements IEquatable<Position> ) and a User who can express such values.

Following your example, something like:

 public sealed class VATIN : IEquatable<VATIN> { // implementation here... } public sealed class Position : IEquatable<Position> { // implementation here... } public sealed class Person { // a few constructors here... // a Person identifier from the domain expert, since it an entity public VATIN Identifier { get { // implementation here } } // some more properties if you need them... public Position CurrentPosition { get { // implementation here } } // some commands public void PromoteTo(Position newPosition) { // implementation here } } public sealed class User { // <summary>Express the position provided according to the culture of the user.</summary> // <param name="position">Position to express.</param> // <exception cref="ArgumentNullException"><paramref name="position"/> is null.</exception> // <exception cref="UnknownPositionException"><paramref name="position"/> is unknown.</exception> public string Express(Position position) { // implementation here } // <summary>Returns the <see cref="Position"/> expressed from the user.</summary> // <param name="positionName">Name of the position in the culture of the user.</param> // <exception cref="ArgumentNullException"><paramref name="positionName"/> is null or empty.</exception> // <exception cref="UnknownPositionNameException"><paramref name="positionName"/> is unknown.</exception> public Position ParsePosition(string positionName) { // implementation here } } 

And don't forget about the documentation and the correctly designed exceptions !

Attention
There are at least two huge design smells in the sample model:

  • public setter (Position property)
  • a System.String with a business value

A public setter means that your object provides its own state to customers regardless of its own invariants, or that such a property has no commercial value for the object and, therefore, should not be part of the object at all. Indeed, mutable objects always need separate commands (which can change state) and queries (which cannot) .

A System.String with business semantics always smells like a domain concept that is left implicit, usually a value object with equality actions (which implements IEquatable, I mean).

Keep in mind that the reusable model is pretty hard to get, as it requires more than two domain experts and extensive ddd modeling experience. The worst “domain model” I have encountered in my career was developed by a senior programmer with huge OOP skills, but had no previous modeling experience: it was a combination of GoF templates and data structures, in the hope that they would be really flexible be useless. After 200 thousand Euros spent on this, we had to throw it away and restart it from scratch.

Maybe you just need a good data model directly mapped to a set of simple data structures in C #: you will never have any ROI from an upfront investment in a domain model unless you really need it!

0
source

It may be worth mentioning the Apache MultiViews function and how to place it based on the Accept-Language header of the browser.

Therefore, if a user requests "content.xml", for example, Apache will deliver content..xml or content.sp.xl or content.fr.xml or something else that is available based on some prioritization rules.

0
source

Given the requirements, I will probably try to model the position myself as an entity / value. This object will not be a translation dictionary, but simply will be used as a key in domainDictionary.

 // IDomainDictionary would be resolved based on CurrentThread.CurrentUICulture var domainDict = container.Resolve<IDomainDictionary<Position>>(); var position = person.Position; Debug.Writeline(domainDict.NameFor(position, pluralForm: 1)); 

Now, assuming that you need to dynamically create new positions when a suitable synonym does not exist, you could probably save the data a bit using IDomainDictionary as a source for automatically filling out sentences in the user interface.

0
source

All Articles