Mostly in various programs I encountered ID strings like “new_game” with separate tables for all languages (and sometimes even as integers in the actual source via constants).
But another solution that I have encountered in a few applications is “gettext”. You write your program with the English string directly, then pass the strings through a “gettext” function (often shortened to `_` in programs I saw).
For the most part these didn't have a great deal of text, and certainly no “writer”. Strings were either what the programmer came up with at the time, or part of the overall UI design.
display(_("My name is %0."), my_name)
For most languages there is a program to scan the source and create/update text files for translations. Along with tools to check for missing or unused translations.
msgid "My name is %0."
msgstr "Je m'appelle %0."
This can also be done for datafiles, not just compiled/script sources.
An interesting feature is that changing the English string, automatically “breaks” the translation as you now have a new “msgid” that needs updating for each language, (e.g. if I rename a menu item, say from “Exit” to “Save and Exit” which in a string ID system I might leave as “menu_exit”), or minor ones like a change in capitalisation and punctuation ("save and exit.", “Save and Exit”), or style (“It will cost 1000s.”, “It will cost thousands.”).
Although any translations I did with ID strings, as far as I am aware all the translators did check to see which strings changed in the English table, not just entirely new keys (maybe they had a compare tool for this, not sure, was outsourced).