PJextra

PJextra

How to approach multi language content management system?

I have a web based software using Gettext for multilanguage support that is working fine. The only limitation is that translations must be done upfront. Now, I’m thinking about letting users insert content in the different languages the system use, so I’m thinking in storing those translations in the database.
I couldn’t find much information about this and I’m not sure about the best approach: creating N schemas (one for each language) that are the same and then in the controller select the one to show? Use a package? I’m using a plug to set the language in the router for Gettext.

Most Liked

belaustegui

belaustegui

Hi!
There are multiple approaches available to solve this problem.

One of them is having a paralell separated schema for storing translations of each model. For example you may have an articles schema and a article_translations schema. This is the traditional approach used by the Ruby gem Globalize. There are some libraries that provide a similar (but I think that not exactly the same) functionality for Elixir.

Having said that, I want to propose you an alternative solution.
Having separated schemas for basic model data and its translations has some difficulties and affects the query performance since it requires multiple JOINs per translated model.

Since modern database support unstructured data such as JSON and Ecto provides support for this kind of data, I’ve built a library that leverages this support for storing model translations into a single column of the same model table.

The library is called Trans, it has some examples and documentation that you may find useful. There is also this article which explains why Trans was created and what improvements does it provide.

Hope you find it useful :sunflower:

11
Post #2
yurko

yurko

we also went the embed way, but did it directly with ecto. Depending on your app if the contents don’t have to be queried (they can, but the syntax is not that nice yet) it’s relatively straightforward with nested embeds. Still not really simple but these things are rarely so :slight_smile:

Here’s a simplified schema

defmodule App.Thing do
  use App.Web, :model

  alias App.Thing.Content
  alias App.OtherThing

  schema "thing" do
    field :name, :string
    field :order, :integer

    embeds_one :en, Content, [on_replace: :delete]
    embeds_one :de, Content, [on_replace: :delete]

    has_many :other_things, OtherThing

    timestamps()
  end

each content can have own embeds one / embeds many. Example query

@spec only_lang(struct(), atom()) :: map()
def only_lang(queryable, lang) do
  queryable
  |> where([t], not(is_nil(field(t, ^lang))))
  |> select([t], %{content: field(t, ^lang), id: t.id, uuid: t.uuid, name: t.name})
end

these fields are nullable so the user can have something for only one supported language. One thing to note is that we mostly deliver contents over the API without having the language in server side state

PJextra

PJextra

After some testing I can see that all suggestions are good options.
Nevertheless, I was thinking in a simpler approach using the new Contexts mindset:..

  1. I’ll have in my schema a duplicated field for each language translations (post, postDe, postFr,…);
  2. In the client I’ll make sure that when this field is being populated translations are also populated (by default with the same original content);
  3. In my Context interface I’ll define each query in a way that depending on the language I’ll search the translations fields, not the original.
    Theoretically I don’t see any problem and it seems very simple logic, as translations are clear (in the schema, client side and queries) and as not all schema fields are suitable for translation I don’t get too much complexity in the schema (making it explicit which fields expect translations).
    Does this make sense for someone that already implemented this functionality or is it just a newbie wrong idea?

Where Next?

Popular in Questions Top

qwerescape
Is there a way to get the call stack or stack trace at any point in the code? Not from exceptions, but an expression that returns how the...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
mgjohns61585
Could someone help me? I’m making my first elixir program, number guessing game. I can’t figure out how to convert the user’s guess from ...
New
johnnyicon
Hi all, I’ve just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I’m trying to use Postgres...
New
Fl4m3Ph03n1x
About me? ( if you have nothing better to do than reading about some random guy in the internet :stuck_out_tongue: ) Hello all, this is ...
New
nobody
How to bind a phoenix app to a specific ip address? could not find anything about that, nowhere, unfortunately, but for me this is quite...
New
LegitStack
I’m trying to make a websocket server in Phoenix or raw Elixir. I heard about gun, I think I could use cowboy, but since I’m not that sma...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
script
If I have a string “1000 cfu/ml” . I want to remove the characters and / and space . So the string is like this "1000" What is the ...
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New

Other popular topics Top

sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
985 42920 311
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
ovidiubadita
Hey all, I discovered Elixir and I love it. I always wanted to learn a functional programming and I intended to go for Haskell, but afte...
New
AngeloChecked
What learn first? Rust or Elixir Hi Elixir community! I’m here because i want learn a new language. I’m a junior developer and mainly i ...
New
aesmail
Hello guys, I have finally made it. I created an admin interface for a framework. It’s been on my todo list for years and with the curre...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
jononomo
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
New

We're in Beta

About us Mission Statement