Author Topic: Non-translated Lua strings: New strategy  (Read 820 times)

Omega

  • MegaGlest Team
  • Dragon
  • ********
  • Posts: 6,167
  • Professional bug writer
    • View Profile
    • Personal site
Non-translated Lua strings: New strategy
« on: 20 July 2011, 03:46:35 »
Yes, this topic has been brought up before, and was denied, presumably to encourage the ability to translate scenarios. However, I return with new logic because, to be honest, we (or at least me) need this feature.

Firstly, why would we need to have a Lua string bypass the translation (if a string isn't translated, it gets a rather ugly "???" surrounding the text, which, while great for testing, doesn't do a whole lot of good to regular players, especially since the untranslated string would be better to display than the untranslated string surrounded by question marks)? Well, let's use an example. In Apocalyptic Dawn (formerly known as Military), the king of hill scenario displays a timer as the display text, counting down from 10:00 (ten minutes). As a result, there are over 600 lines just from this timer. Personally, I had to write a script to generate those lines even, and many modders would just get stumped on how to proceed.

As another example, suppose the display text dynamically tracks enemy kills, and lists them. If the enemies spawn using a formula for infinity (ie, Call of Duty Black Op's zombie mode), there's no saying how many kills they'll get. We could end up just having a language file of 1000 lines that look like "1=1, 2=2, 3=3"... We shouldn't have to do this.

But before we think that this is just for numbers, suppose we output the name of the last unit to die (this is done with lastDeadUnitName()) into the display text. Or if we ever get a way to allow players to input data (eg, type their name), we'd want to output that non-translated too.



So that sums up why we need this simple feature, now as to how it can be done. Firstly, I propose two methods. The first is simplest, though would require the entire output be non-translated, while the second would allow some translation. Obviously, the second is more difficult, though would allow translation to still work by only bypassing the translator for the variable. However, it should be noted that no mods actually are in anything but english (GAE's customized magitech, being bundled with the engine, is not a mod)! Besides, this feature would be used rather rarely, only when it is necessary to bypass the translation, such as in the examples listed above.

First method:
Firstly, we can simply set the display text or post a message by using an optional attribute in the function. Instead of showMessage(text, header) and setDisplayText(text), we'd have showMessage(text, header, [translate]) and setDisplayText(text, [translate]) (where translate is an optional boolean value which defaults to true, but if false, will bypass the censor). The advantage is a backwards compatible, easy to use method of telling a string not to be translated. The additional attribute would even be ignored on an older version of GAE, preventing that from breaking.

Second method:
Now the more complicated method: a way to have some parts of the string translated and some not. The string concatenation unit in Lua is .. (thus, to combine the strings 'Cat' and 'Dog', we could use 'Cat' .. 'Dog'). Thus, we'd use the above method of not translating a string, but instead translating parts of the string using a new function (nested), and then combine all these parts, some translated and some not, into our new string, which we'd output.

This would need a new command such as translate(text) that would translate the text and return the translation (and old school, if there is no translation, we'd see it surrounded in question marks). I'm thinking an example is worth a few dozen words:

Code: [Select]
showMessage('Do not translate' .. translate('translate_me') .. 'Do not translate', translate(header), 0);
So how does that work? Firstly, the first attribute is the text of our message, where we have a non translated string at the beginning and end (since it isn't passed through the translate() function), then we have a translated string in the middle (it is passed through this function). Thus what the engine does is it perform proper nesting, first translating the "translate_me" string from the language file and "merging" it with our non-translated text to make our finally line. We must translate the header too, since we'll be turning translation of the final product off. Finally, since we already did the translation needed, we set our last attribute, as described above in the first method, to false, meaning it will not try to translate this line (it's already translated, thanks to the translate() function.

This isn't a whole lot more difficult, and expands on the first method, so it's wholly possible to do just the first method and then come back to the second method later.

For the need of a better example of it in action, let's use this example. Here, we set the display text to tell the number of kills and the name of the last killed unit. For simplicity of the example, the number of kills and last killed unit are just static variables, but in an actual scenario, they could be dynamic variables that will be changing with each kill.

Code: (Lang) [Select]
string_beginning = Total units killed so far:
string_middle = Last killed unit was a

Code: (Lua) [Select]
kills = 19;
lastUnitKilled = 'swordman';

setDisplayText(translate('string_beginning') .. kills .. '(' .. translate('string_middle') .. lastUnitKilled .. ').', 0);

The important part of this above example is: translate('string_beginning') .. kills .. '(' .. translate('string_middle') .. lastUnitKilled .. ').' This would output Total units killed so far: 19 (Last killed unit was a swordman)..

I cannot think of any flaws with this strategy, as I even checked to ensure that you can concatenate strings as attributes to functions (eg, myFunction('1' .. '2');) and that Lua performs coercion (converting numbers to strings when performing concatenation), eg, myFunction('string' .. 2);.



To sum that all up, there are two changes. The first is simply adding an attribute to setDisplayText() and showMessage() which will allow the translator to by bypassed. The second is adding a new function translate() to grab the translated version of the string. Things like concatenation and coercion are a standard part of Lua and should already exist in the engine. Note that the second function requires the first change to be of any use.

If these changes occur, it would make things like timers far, far easier in scenarios, and as per a very interesting idea on the Apocalyptic Dawn thread, there will certainly be some interesting scenarios to come from this! As long as the second method is chosen and modders 'play within the rules' by not abusing the bypass of the translator when not needed (eg, don't bypass anything unless it is not static, that is, not created during the game, such as a timer or kill count), there will be no negative impact on anyone wishing to translate the scenario, except possibly in languages where the sentence structure may be different and the variable would normally be in a different spot, which is a small compromise for the advantages this offers.
Edit the MegaGlest wiki: http://docs.megaglest.org/

My personal projects: http://github.com/KatrinaHoffert