So how do you do it?There isn't really a general solution to achieve i18n. There are many different ways and you have to choose which is actually best for you. If you do have a relatively small amount of data and ordering by translations is not necessary you can store "keys" in your tables that are being translated by Spring i18n for example. Spring i18n could store the translations in separate property files. Since you wont have to worry about performance, this is a very easy way to achieve i18n.
But lets say you have lots of data, and you need to order it on the database side. With such requirements you are better off storing translations inside your database tables. While there are guides, and also an opensource framework on the internet that implement i18n with one centralized translation-table that holds all translations i highly recommend not to follow that idea. The problem with this solution is, that you will have to add one more join for every field that has to be translated, whereas multiple joins for a single table with lots of data can easily become a performanceissue.
Its better to follow the approach to create a separate translation-table for every entity. This means that you would have one entity containing simple data, and another one containing only the fields that require translation. Like so:
This schema is pretty normalized and you only have a single join on fetching an entity, while you can still order appropriate.
Your entities would look like this:
You could then fetch a book with its translations with:
If you cannot accept your ServiceLayer to deliver two entities for every Book only because of translations, i suggest you to implement some DTO like this BookTO which holds all values.
You'd just need to map the values of Book and BookTranslation to BookTO within your ServiceLayer and also return BookTO instead of Book in your ServiceLayer.
This solution might not always be the most appropriate, but it definitely comes in handy sometimes.