3

I've been trying to build this (principally desktop, but could eventually be turned into a cloud app) document editor program for a while and have it laid out using the broad principles of the 3-Layer (Data, Model, and UI) and MVC (Model-View-Controller) patterns, and one thing I've often run into is where to best put little odds-and-ends bits of logic that seem to inherently cross more than one layer.

For example, in any classic desktop document editor, when you create a new document, it typically ends up with a "name" shown on its window or tab, often something like "Untitled" or "Untitled Document" - most especially though, followed by a counter, such as "Untitled Document 3" (counting off how many new documents you've created). This obviously requires one of those text strings to be in the program, somewhere, and presented to the user. This label, of course, gets replaced when you "Save" the document to a file.

Right now, the way I have it is that these names are generated in the Model layer, i.e. the logic that creates a new document hard codes the string "Untitled Document" or similar in the business logic layer and then appends the counter. Trouble is:

  1. this "feels" like an inherently UI-related concern,
  2. but because the string involves a counter that is part of the document creation logic, we must generate that string dynamically in connection therewith, and thus feels most like we need to push it down to the Model layer, but then
  3. internationalization - what if we want to supply a translation to a foreign language? Given the model layer is agnostic to the UI layer, and the UI layer may (does!) use a widget system with its own feature to supply the translated strings for things wholly in that layer, we could easily end up with at least 2 different methodologies of translation at the same time, which "feels wrong" or "like a mess".

How should this be handled? Where should this kind of seemingly cross-cutting logic go?

The_Sympathizer
  • 447
  • 3
  • 9

2 Answers2

4
  1. this "feels" like an inherently UI-related concern,

Definitely not. Thought experiment: next major release you want to change your UI technology - the title string for the unsaved document will still be the same. Hence, to me the model layer "feels" perfectly right, UI "feels" wrong.

  1. but because the string involves a counter that is part of the document creation logic, we must generate that string dynamically in connection therewith, and thus feels most like we need to push it down to the Model layer, but then

As I said, the model layer feels right. I would actually use a string template like "Untitled Document %n", where %n gets replaced at run time. The template will then become subject to internationalization.

  1. internationalization - what if we want to supply a translation to a foreign language?

Internationalization is a cross cutting concern - it typically crosses more than one layer. Your case is an example showing that the classic 3 layer architecture for business applications ("Data, Model, and UI") isn't describing your application in full, it will often cover only 80 to 90% of the code (maybe less, depending on the kind of software you write). In any real world application there are usually infrastructure components which don't belong to any of those layers (or to all of them). Some of those infrastructure components might be 3rd party components, some of them you may have to develop by yourself.

Given the model layer is agnostic to the UI layer, and the UI layer may (does!) use a widget system with its own feature to supply the translated strings for things wholly in that layer, we could easily end up with at least 2 different methodologies of translation at the same time, which "feels wrong" or "like a mess".

When your widget system provides such a translation system which can be used for the UI layer exclusively, the translation system is crap and you should find a better one which does not have this restriction.

Of course, when the UI provides a very convenient way for localization of all widget strings, but nothing else, you may live with the restriction that you need two translation systems. That's a trade-off, maybe it is acceptable in your case, maybe not. If that bothers you too much, you could try to find a way to extend the translation feature of your widget system to be used in all other layers, or find a way to feed two different translation systems from the same translation table. But it should not necessarily have influence on the layer where dynamic, user-readable strings will be created.

Doc Brown
  • 199,015
  • 33
  • 367
  • 565
3

How I've done it before is by having a code construct (class/interface, function...) that is able to answer "Give me the translation for this string key in the current user's language".

Depending on your needs, multiple implementations of that construct, for the front, back end, etc. might be inevitable. Note that there might be value in keeping the implementations as similar as possible, even if they use different languages/stacks. Of course, if you're lucky enough to have an isomorphic language for front and back-end, all the easier.

For variable bits inserted inside internationalized strings, I would provide overrides of the translator able to inject 1, 2, 3, ... params in the translation. Add markers in the string key to indicate the number and order of params, and a placeholder in the translated value. Something like i18n.documents.untitled.{nb} -> "Untitled document {nb}".

guillaume31
  • 8,358
  • 22
  • 33