Working lua need advice deciding which method is best

Discussion of Lua and LuaWML support, development, and ideas.

Moderator: Forum Moderators

User avatar
Spannerbag
Posts: 538
Joined: December 18th, 2016, 6:14 pm
Location: Yes

Working lua need advice deciding which method is best

Post by Spannerbag »

Hi,
I've finally bitten the bullet and decided to make a coherent attempt at learning lua.
It's slow, but I'm making progress.
I have some very basic lua that works, I'd just like to sanity check my approach.

All I wanted the code to do initially was confirm the value of some lua variables in a [message] - and it's this bit I want to check.

Here's the lua:

Code: Select all

local _ = wesnoth.textdomain "wesnoth-stub"

-- Pass values from WML to lua 
function wesnoth.wml_actions.dbck_setup(cfg)
  local event_name  = cfg.event_name     or wml.error "[dbck_setup] missing required event_name= attribute."
  local side_candc  = tonumber(cfg.side) or wml.error "[dbck_setup] missing required side= attribute."
  local unit_filter = cfg.unit_filter    or wml.error "[dbck_setup] missing required unit_filter= attribute."
  wml.fire("message",
  { speaker = "narrator", message = stringx.vformat(_ "dbck_setup values in lua:\nevent_name=" .. event_name .. "\nside_candc=" .. side_candc .. "\nunit_filter=" .. unit_filter)})
end
This all works fine, as does an alternative message format (with added line breaks to aid legibility):

Code: Select all

  wml.fire("message",
{ speaker = "narrator", message = stringx.vformat(_ "dbck_setup values in lua:
\nevent_name=$eventname\nside_candc=$sidecandc\nunit_filter=$unitfilter",
{eventname=event_name,sidecandc=side_candc,unitfilter=unit_filter})})
I prefer the first (concatenation) method however I've read on stackoverflow that this is inefficient.

I'd like to use a stringx.join as I presume this is more efficient but that wants a list and I couldn't find a (sane) way to get that to work with simple strings.

So, my question is this: what's the best (i.e. most efficient and preferably most terse) form of this logic?

Thanks in advance for your time and trouble.

Cheers!
-- Spannerbag
SP Campaigns: After EI (v1.14) Leafsea Burning (v1.17, v1.16)
I suspect the universe is simpler than we think and stranger than we can know.
Also, I fear that beyond a certain point more intelligence does not necessarily benefit a species...
gnombat
Posts: 710
Joined: June 10th, 2010, 8:49 pm

Re: Working lua need advice deciding which method is best

Post by gnombat »

Spannerbag wrote: April 10th, 2024, 11:12 am I prefer the first (concatenation) method however I've read on stackoverflow that this is inefficient.
I would not worry about this unless you are (a) inside a loop, and (b) performing concatenation on the same string over and over again inside that loop.

Performing a single string concatenation, or even five string concatenations (as in your first example), is unlikely to cause performance problems.

There are other problems with your first example, though:
  1. It is using both stringx.vformat and concatenation. This doesn't really make sense - normally you would use one or the other, but not both.
  2. It isn't translatable. (What you're doing with _ won't really work right.) Since it appears to be just for debugging purposes maybe this isn't an issue, but if you want your output to be translated it is probably better to avoid concatenation and just use stringx.vformat (like in your second example).
User avatar
Celtic_Minstrel
Developer
Posts: 2241
Joined: August 3rd, 2012, 11:26 pm
Location: Canada
Contact:

Re: Working lua need advice deciding which method is best

Post by Celtic_Minstrel »

You don't need to make it translatable at all. Just concatenate if that feels easier for you, but keep in mind that when you do need something to be translatable that you must use stringx.vformat (or string.format is fine too).
Author of The Black Cross of Aleron campaign and Default++ era.
Former maintainer of Steelhive.
User avatar
Spannerbag
Posts: 538
Joined: December 18th, 2016, 6:14 pm
Location: Yes

Re: Working lua need advice deciding which method is best

Post by Spannerbag »

gnombat wrote: April 10th, 2024, 12:55 pm ...There are other problems with your first example, though:
  1. It is using both stringx.vformat and concatenation. This doesn't really make sense - normally you would use one or the other, but not both.
  2. It isn't translatable. (What you're doing with _ won't really work right.) Since it appears to be just for debugging purposes maybe this isn't an issue, but if you want your output to be translated it is probably better to avoid concatenation and just use stringx.vformat (like in your second example).
Thanks for taking the trouble to reply, much appreciated.

I'm very much a beginner here so please bear with me.
First, why does using both stringx.vformat and concatenation not make sense?
Can stringx.vformat also concatenate and I missed it?
Or is there some other reason?

Also, what am I doing wrong with _?
Is it in the wrong place or needs a comma or brackets somewhere or what?
I have no idea I'd got that wrong so have no idea how to remedy it!

Sorry to be dim but this is all very new to me.
Thanks again for your help.
Cheers!
-- Spannerbag
SP Campaigns: After EI (v1.14) Leafsea Burning (v1.17, v1.16)
I suspect the universe is simpler than we think and stranger than we can know.
Also, I fear that beyond a certain point more intelligence does not necessarily benefit a species...
User avatar
Spannerbag
Posts: 538
Joined: December 18th, 2016, 6:14 pm
Location: Yes

Re: Working lua need advice deciding which method is best

Post by Spannerbag »

Celtic_Minstrel wrote: April 10th, 2024, 1:30 pm You don't need to make it translatable at all. Just concatenate if that feels easier for you, but keep in mind that when you do need something to be translatable that you must use stringx.vformat (or string.format is fine too).
Thanks for taking the trouble to reply.
It's not so much that concatenate feels easier but the second method involved creating local variables just for a message which seemed to me to be unnecessarily complicated and wasteful - surely there's an easier, quicker and more elegant way to do this?
Ideally I'd like a way to create a [message] in lua that has (lua) variables embedded in it as simply as possible without extra bells and whistles.

As in my previous post this is all new to me and I don't have a sense of what's right and wrong yet.
I can probably hack out code that works but I don't want to develop bad habits early on!

Again thanks for your advice.

Cheers!
-- Spannerbag
SP Campaigns: After EI (v1.14) Leafsea Burning (v1.17, v1.16)
I suspect the universe is simpler than we think and stranger than we can know.
Also, I fear that beyond a certain point more intelligence does not necessarily benefit a species...
gnombat
Posts: 710
Joined: June 10th, 2010, 8:49 pm

Re: Working lua need advice deciding which method is best

Post by gnombat »

Spannerbag wrote: April 10th, 2024, 2:25 pm First, why does using both stringx.vformat and concatenation not make sense?
Can stringx.vformat also concatenate and I missed it?
Well, yes? Isn't that basically what your second example was doing - concatenating strings using stringx.vformat, without using the concatenation operator (..)?

I'm just going to try to simplify your examples a bit (skipping the translation for now):

Code: Select all

local x = 1
local y = 2
wml.fire("message", { speaker = "narrator", message = "X is " .. x .. " and y is " .. y })
wml.fire("message", { speaker = "narrator", message = stringx.vformat("X is $x and y is $y", {x=x,y=y}) })
wml.fire("message", { speaker = "narrator", message = string.format("X is %d and y is %d", x, y) })
Those three messages should all contain the exact same output: X is 1 and y is 2. The first one uses the concatenation operator (..). The second one uses stringx.vformat and does not use the concatenation operator. The third one is similar to the second but uses string.format.

There isn't really any reason you would want to use both the concatenation operator .. and stringx.vformat together.

Does that make sense?
User avatar
Spannerbag
Posts: 538
Joined: December 18th, 2016, 6:14 pm
Location: Yes

Re: Working lua need advice deciding which method is best

Post by Spannerbag »

gnombat wrote: April 10th, 2024, 2:59 pm
Spannerbag wrote: April 10th, 2024, 2:25 pm First, why does using both stringx.vformat and concatenation not make sense?
Can stringx.vformat also concatenate and I missed it?
Well, yes? Isn't that basically what your second example was doing - concatenating strings using stringx.vformat, without using the concatenation operator (..)?

I'm just going to try to simplify your examples a bit (skipping the translation for now):

Code: Select all

local x = 1
local y = 2
wml.fire("message", { speaker = "narrator", message = "X is " .. x .. " and y is " .. y })
wml.fire("message", { speaker = "narrator", message = stringx.vformat("X is $x and y is $y", {x=x,y=y}) })
wml.fire("message", { speaker = "narrator", message = string.format("X is %d and y is %d", x, y) })
Those three messages should all contain the exact same output: X is 1 and y is 2. The first one uses the concatenation operator (..). The second one uses stringx.vformat and does not use the concatenation operator. The third one is similar to the second but uses string.format.

There isn't really any reason you would want to use both the concatenation operator .. and stringx.vformat together.

Does that make sense?
Yes :D
First off, thanks for the example code, good to see all options grouped together.
That makes sense, what I was hoping might be possible would be to have something like the stringx.vformat but without the {...} bit.
I suppose I was hoping to be able to have a construct like:

wml.fire("message", { speaker = "narrator", message = "X is ?x and y is ?y })

where "?" is an operator like "$" in WML, i.e. "use the value of this variable".
I just wanted to be sure there wasn't a way of doing this (or a clever syntactical short-cut) that involved neither .. nor {...}*.

Clearly there isn't, or you'd've mentioned it so I know my options now. :)
So thanks for your patience and clear examples, they helped. A lot.


* - What is the correct term for doing a {...} btw?

Oh, no pressure or anything but if you do get chance (and aren't utterly bored with my stupidity) I'd still like to know what it was with _ I got wrong.

Let's see what I can break next...

Cheers!
-- Spannerbag
SP Campaigns: After EI (v1.14) Leafsea Burning (v1.17, v1.16)
I suspect the universe is simpler than we think and stranger than we can know.
Also, I fear that beyond a certain point more intelligence does not necessarily benefit a species...
gnombat
Posts: 710
Joined: June 10th, 2010, 8:49 pm

Re: Working lua need advice deciding which method is best

Post by gnombat »

Spannerbag wrote: April 10th, 2024, 7:52 pm * - What is the correct term for doing a {...} btw?
A table constructor.
Spannerbag wrote: April 10th, 2024, 7:52 pm Oh, no pressure or anything but if you do get chance (and aren't utterly bored with my stupidity) I'd still like to know what it was with _ I got wrong.
_ is a function which should always take as its argument a single literal quoted string, which should generally be a complete sentence rather than a fragment of a sentence.

Suppose you have two variables you want to use in a sentence:

Code: Select all

local color = "red"
local species = "dragon"
local sentence = "There is a " .. color .. " " .. species .. "."
If you want to make that translatable, you should not use concatenation:

Code: Select all

# WRONG: the argument to _() should be a single literal quoted string
local sentence = _("There is a " .. color .. " " .. species .. ".")
Note that this doesn't work either:

Code: Select all

# ALSO WRONG: the argument to _() should be a literal quoted string, not a variable
local sentence = "There is a " .. color .. " " .. species .. "."
sentence = _(sentence)
This might sort of look like it will work, but it doesn't really - it might work when translating to some languages, but you will run into problems for other languages:

Code: Select all

# ALSO WRONG: the argument to _() should be a complete sentence, not a fragment of a sentence
local sentence = _("There is a ") .. color .. " " .. species .. "."
Really the only way to do it is to avoid the concatenation operator (..):

Code: Select all

# THIS WORKS: the argument to _() is a single literal quoted string which represents a complete sentence
local sentence = stringx.vformat(_("There is a $color $species."), {color=color,species=species})
User avatar
Ravana
Forum Moderator
Posts: 3018
Joined: January 29th, 2012, 12:49 am
Location: Estonia
Contact:

Re: Working lua need advice deciding which method is best

Post by Ravana »

If you used global variables then stringx.vformat(_("There is a $color $species."), _G) something of this idea will work. _G is table of global variables.

With local you need to explicitly add them.

Or use local v={}, v.color = "red", v.species = "dragon", stringx.vformat(_("There is a $color $species."), v)
gnombat
Posts: 710
Joined: June 10th, 2010, 8:49 pm

Re: Working lua need advice deciding which method is best

Post by gnombat »

Ravana wrote: April 10th, 2024, 9:03 pm If you used global variables then stringx.vformat(_("There is a $color $species."), _G) something of this idea will work. _G is table of global variables.
The stringx.vformat documentation also suggests using WML variables - then this should work:

Code: Select all

stringx.vformat(_("There is a $color $species."), wml.all_variables)
User avatar
Celtic_Minstrel
Developer
Posts: 2241
Joined: August 3rd, 2012, 11:26 pm
Location: Canada
Contact:

Re: Working lua need advice deciding which method is best

Post by Celtic_Minstrel »

gnombat wrote: April 10th, 2024, 2:59 pm There isn't really any reason you would want to use both the concatenation operator .. and stringx.vformat together.
I wouldn't say there is no reason at all, but the case discussed in this thread is definitely not a reason to use them together.

What there's not point in doing is calling vformat with just a string as argument and no variable definitions. You're calling it just for it to do nothing because there was nothing to substitute. (Although in fact it'll delete all the variable references from the string, I think – or substituted default values where specified.)
Ravana wrote: April 10th, 2024, 9:03 pm If you used global variables then stringx.vformat(_("There is a $color $species."), _G) something of this idea will work. _G is table of global variables.
This doesn't actually work, as vformat requires a WML table as the argument.
Author of The Black Cross of Aleron campaign and Default++ era.
Former maintainer of Steelhive.
white_haired_uncle
Posts: 1226
Joined: August 26th, 2018, 11:46 pm
Location: A country place, far outside the Wire

Re: Working lua need advice deciding which method is best

Post by white_haired_uncle »

Code: Select all

_("There is a $color $species.")
I only speak American, not even English, but that looks like a pain for translators. Would they re-order $color and $species as necessary (in which case this appears to be a preferable alternative to string.format, which I find much more readable)? What about gender (a -> un or una depending on the unknown gender of $species). And even in American, a could be an for color=azure (I know I ran into a tool that handles that, though not in wesnoth, strings in Qt I think?).

I don't mean to divert the thread, but it does seem like the choice the of "best" string formatting tool may depend on the string, particularly when translation is important.

Of course this is all academic since we should all be using sprintf-like tools, since that's the way I learned and therefore anything that works differently is wrong.
Speak softly, and carry Doombringer.
gnombat
Posts: 710
Joined: June 10th, 2010, 8:49 pm

Re: Working lua need advice deciding which method is best

Post by gnombat »

white_haired_uncle wrote: April 11th, 2024, 2:14 am

Code: Select all

_("There is a $color $species.")
I only speak American, not even English, but that looks like a pain for translators. Would they re-order $color and $species as necessary (in which case this appears to be a preferable alternative to string.format, which I find much more readable)?
Yes, that's the idea - the translators could change the order of the variables for languages in which the noun would appear before the adjective.
white_haired_uncle wrote: April 11th, 2024, 2:14 am What about gender (a -> un or una depending on the unknown gender of $species). And even in American, a could be an for color=azure
Yes, there could be other issues, too, but fortunately there is no such thing as an ...

https://en.wikipedia.org/wiki/Azure_Dragon

:augh:
User avatar
Celtic_Minstrel
Developer
Posts: 2241
Joined: August 3rd, 2012, 11:26 pm
Location: Canada
Contact:

Re: Working lua need advice deciding which method is best

Post by Celtic_Minstrel »

white_haired_uncle wrote: April 11th, 2024, 2:14 am

Code: Select all

_("There is a $color $species.")
I only speak American, not even English, but that looks like a pain for translators. Would they re-order $color and $species as necessary (in which case this appears to be a preferable alternative to string.format, which I find much more readable)? What about gender (a -> un or una depending on the unknown gender of $species). And even in American, a could be an for color=azure (I know I ran into a tool that handles that, though not in wesnoth, strings in Qt I think?).
None of that is handled, which is why you shouldn't construct translatable strings in this way. The only things that are safe to be substituted into a translatable sentence are numbers and proper nouns, and in the latter case you still need to have two copies of the string – one feminine and one masculine. (Substituting whole paragraphs into a string, or in some case full sentences, may also be fine.)

You could certainly write code to automatically handle substitutions like this example. It would be complicated, and it would require different code for every language. Basically, the program would need to fully understand the systems of noun declension and verb conjugation in the target language. But Wesnoth's translation system makes no attempt to do this, so the solution is to keep your substitutions simple.
Author of The Black Cross of Aleron campaign and Default++ era.
Former maintainer of Steelhive.
gnombat
Posts: 710
Joined: June 10th, 2010, 8:49 pm

Re: Working lua need advice deciding which method is best

Post by gnombat »

Celtic_Minstrel wrote: April 11th, 2024, 4:11 am
white_haired_uncle wrote: April 11th, 2024, 2:14 am

Code: Select all

_("There is a $color $species.")
I only speak American, not even English, but that looks like a pain for translators. Would they re-order $color and $species as necessary (in which case this appears to be a preferable alternative to string.format, which I find much more readable)? What about gender (a -> un or una depending on the unknown gender of $species). And even in American, a could be an for color=azure (I know I ran into a tool that handles that, though not in wesnoth, strings in Qt I think?).
None of that is handled, which is why you shouldn't construct translatable strings in this way. The only things that are safe to be substituted into a translatable sentence are numbers and proper nouns, and in the latter case you still need to have two copies of the string – one feminine and one masculine. (Substituting whole paragraphs into a string, or in some case full sentences, may also be fine.)
I'm not sure what you mean by "none of that is handled" - the point I was trying to make with that example is that re-ordering the variables is handled, which is why using a formatting function may be preferable to simply using the concatenation operator. Of course, there may be other translation issues that are not so easy to solve.
Post Reply