I'm going to try to make that a reality.
Actually, I just remembered something about stringification macros. You can have the strings but keep the benefits of having enums if you like. There are various approaches to that.
This is one way to do it, which I'd recommend since it's straightforward and doesn't use any bleeding edge C++14 trickery that nobody will understand:
http://www.jakeshirley.com/2014/11/In this example, you have an input file, the example given is this:
FRUIT(Banana)
FRUIT(Apple)
FRUIT(Strawberry)
FRUIT(Pineapple)
FRUIT(Snozberry)
The trick here is that each CPP file is free to define the macro "FRUIT" any way they like. e.g. this file creates a fruit enum:
// Fruit Enum
#define FRUIT(x) x,
enum Fruit
{
#include "FruitDeclarations.h"
Count
};
#undef FRUIT
And this file creates an array of fruit strings:
// Fruit Strings
#define FRUIT(x) #x,
const char *FruitStrings[] = {
#include "FruitDeclarations.h"
"Invalid"
};
#undef FRUIT
... you can see where this is going. You get the benefit of still using the enums for the hard-coded parts, which ensures that compile-time checking picks up typos, but you get all the strings for the tokens auto-generated, to match, at compile time, so they can be spit out by the program and also used for parsing a translation file.
To make this approach work for LCS, you'd need to refactor in several steps
first you need to get rid of the
individually named strings in favor of an array referenced via enums. To make that easier, you can use a std::map (what follows is entirely pseudocode so YMMV)
So, you have the list of enums:
enum string_tokens =
{
WILL_STEAL,
WILL_EMBEZZLE,
(etc)
}
and a map
std::map string_values =
{
{WILL_STEAL, "String for will steal"},
{WILL_EMBEZZLE, "String for will embezzle"}
{etc, etc}
}
Note, that all direct accesses to these string literals must now be passed through the map lookup, but preferably through a new function to keep the boiler-plate to a minimum.
However, this part could be touchy since you'll need to convert every call to drawing the strings directly into a call to the "lookup the needed string" function. If they're not all changed, it could try and print out the numerical value of the enums and not the strings. So, if you're going to rename the tokens, this would be the point at which to do it, to ensure the compiler picks up everywhere you need to change to the new string-lookup-function.
After this is achieved, then the code can be refactored again using the code from the FRUIT example given above: make a separate file called string_tokens.h and move all the enum names in there written as:
S(WILL_STEAL)
S(WILL_EMBEZZLE)
etc
(calling the macro "S" will minimize the boiler plate. I'm hoping to find better methods that will do away with the need for the S-macros completely, which I believe exist but can't find).
After that, the game can be modded so that it also generates the string array from this file (as shown in the FRUIT example) and at run-time it then looks for translation files, and parses them against the keys, over-writing any string in the map which matches a key. This would allow anything from partial fixes/mods to complete text rewrites for users without compiling. Also, a benefit for the main developers: if someone reports a typo, there's no need to recompile, merely have a typo-fixing translation file that's pulled in before any other translation file, and you can issue short-term patches without needing recompilation. Typos would then be folded into the code once the next code version is released.
Another benefit of having all the string literals in a map or table is that you can write a function to dump out all the moddable text from the game at runtime, for example, a "-dumpstrings" command-line option, but it can be expanded with a few sub-parameters to expand the usefulness. Here are some ideas about how that could work:
LCS -dumpstrings // dump all tokens and current strings (after translation)
LCS -dumpstrings changed // only dump ones where a file changed the string
LCS -dumpstrings not-changed // only dump ones that were not changed by a file
LCS -dumpstrings default // dump the original/default strings - would stack with changed/not-changed
LCS -dumpstrings tokens // only dump token names - again, would stack with changed/not-changed
LCS -dumpstrings both // show tokens, plus both default and translated strings.
I think that set of command line parameters relating to the strings would give people all the tools they need to quickly see what needs to be updated if the version changes and they're working on a language / mod.