Hello, Juce-ys. I have an interesting C+±y design question relating to Juce.
I’m working on internationalizing our application, which has been going very well. I would like to extract a list of strings that need to be translated from it.
About half the strings are embedded in the code, and about half of them occur as part of structures within external data files.
One solution would be grepping and rewriting the grep results to get the strings out of the code, and then writing a data extractor for the data files.
Writing a completely reliable data extractor is easy, but it’s another moving part - writing the completely reliable grep/rewrite is tricky, as some of my strings are broken up into multiple source lines (I could fix that but suddenly I’d have a few reallylonglines when I so far have managed to keep everything tall and thin…), and some of my strings contain quote characters and some are in comments…
What I’m doing is to intercept all calls to translate() and store them, then emitting them to a file at shutdown, which I think is much neater.
Easy and effective, except that any message in a codepath that’s never run is not stored to the file. There are rather a lot of code paths, and a lot of these are errors which are somewhat difficult to arrange to have happen. I particularly don’t want the unpleasant workload of “making a few text changes/spending 15 minutes exercising every code path”.
So, of course, I want to do this automatically. Unfortunately, the only solution I can come up with uses global variables of class type, which I know is a minefield. I would naturally avoid this, but I can’t see another way to do it. On the other hand, I’m fairly familiar with the traps regarding global variables of class type, and I don’t believe that my approach will encounter them…
Here’s the code…
[code]class TranslatedString {
public:
TranslatedString(const char* o, bool translateNow = false) : original_(o) {
if (translateNow)
translate();
else
STRINGS.push_back(this);
}
operator const String&() const {
return *translated_;
}
static void translateAll() {
for (uint i = 0; i < STRINGS.size(); ++i)
STRINGS[i]->translate();
}
private:
void translate() {
translated_.reset(new String(trans(original_)));
}
typedef std::vector<TranslatedString*> StringList;
static StringList STRINGS;
const char* const original_;
ptr translated_;
DISALLOW_COPY_ASSIGN_AND_LEAKS(TranslatedString);
};
[/code]
The idea is that your strings are either static instances of TranslatedString, which are translated when your start-up routine calls TranslatedString::translateAll() - or they’re dynamic instances where translateNow is true, and the translation is called at construction time.
There is no possibility of dependencies between TranslatedStrings, and there are no other static variables of class type, so there won’t be other static classes depending on TranslatedString.
There isn’t a mutex corresponding to STRINGS, because all the additions occur in a single main thread, and then the translations occur at one spot, absolutely after that. (I could put in a mutex if it came down to it, it’s not important…)
But it still worries me. What do you think? Is there a better way?