One way is to have different version (my.app/en, my.app/de), but I find this overwhelming to maintain.
Another way is to use a “lookup table” in a database populating variables on-the-fly.
That’s essentially the approach I typically take but by keyword instead of word, and the table is structured to key by language/keyword. Doing it this way allows you to add languages without doing table changes.
lang keyword valuetext
------------------------------------------------------
eng SitePointIsCool SitePoint is Cool
es SitePointIsCool SitePoint es genial
de SitePointIsCool SitePoint ist cool
kr SitePointIsCool SitePoint는 멋지다
Potentially usable: Internationalization - Mozilla | MDN
(strictly speaking, the page is describing its use for browser extensions, but the functions are available to Javascript in general…)
It also references i18n.js as a library for usage.
I would not suggest to put translations in a database as it is hard to maintain.
The i18next solution with JSON files is what I prefer also. You can easily sent the files to translators if needed and even non developers can change the translations with a small editor.