jptrrs / SpeakUp

A conversation mod for RimWorld
4 stars 23 forks source link

Use `defName` not `label` for grammar rules for multilang support #32

Open alattalatta opened 3 years ago

alattalatta commented 3 years ago

One can argue that rules (in context of scripting that is ruleStrings in RimWorld) should be a thing of invariance. And as you may know it, labels do change, especially when the user is changing language of the game.

https://github.com/jptrrs/SpeakUp/blob/bb8d7ed3ce960ee2548f1270331481b2d6489e8c/SpeakUp/ExtraGrammarUtility.cs#L99

https://github.com/jptrrs/SpeakUp/blob/bb8d7ed3ce960ee2548f1270331481b2d6489e8c/SpeakUp/ExtraGrammarUtility.cs#L114-L115

https://github.com/jptrrs/SpeakUp/blob/bb8d7ed3ce960ee2548f1270331481b2d6489e8c/SpeakUp/ExtraGrammarUtility.cs#L156

These rules all change when you change your language. Of course, translations should also include the rule in its whole form so one should be able to do this: (Note: Actually I can't, described below)

// English
r_logentry(INITIATOR_research_level>=10)->[someOtherToken]

// Korean
r_logentry(INITIATOR_연구_level>=10)->[someOtherToken]
//                    ^^ localized skill name

But in Korean (and supposedly almost all other languages), this does not work. See, this is Verse.Grammar.Rule_String.pattern:

private static Regex pattern = new Regex("\r\n\t\t# hold on to your butts, this is gonna get weird\r\n\r\n\t\t^\r\n\t\t(?<keyword>[a-zA-Z0-9_/]+)\t\t\t\t\t# keyword; roughly limited to standard C# identifier rules\r\n\t\t(\t\t\t\t\t\t\t\t\t\t\t# parameter list is optional, open the capture group so we can keep it or ignore it\r\n\t\t\t\\(\t\t\t\t\t\t\t\t\t\t# this is the actual parameter list opening\r\n\t\t\t\t(\t\t\t\t\t\t\t\t\t# unlimited number of parameter groups\r\n                    [ ]*                            # leading whitespace for readability\r\n\t\t\t\t\t(?<paramname>[a-zA-Z0-9_/]+)\t# parameter name is similar\r\n\t\t\t\t\t(?<paramoperator>==|=|!=|>=|<=|>|<|) # operators; empty operator is allowed\r\n\t\t\t\t\t(?<paramvalue>[^\\,\\)]*)\t\t\t# parameter value, however, allows everything except comma and closeparen!\r\n\t\t\t\t\t,?\t\t\t\t\t\t\t\t# comma can be used to separate blocks; it is also silently ignored if it's a trailing comma\r\n\t\t\t\t)*\r\n\t\t\t\\)\r\n\t\t)?\r\n        [ ]*                                        # leading whitespace before -> for readability\r\n\t\t->(?<output>.*)\t\t\t\t\t\t\t\t# output is anything-goes\r\n\t\t$\r\n\r\n\t\t", RegexOptions.ExplicitCapture | RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace);

It indeed is weird, but nothing is relevant except this: (?<paramname>[a-zA-Z0-9_/]+) Parameters MUSE use ASCII alphabets. This makes it impossible to translate SpeakUp even to other latin alphabet languages with extra characters.

Unknown parameter INITIATOR_ in rule game_chess(INITIATOR_연구_level>=10)->[chess_smart]

Note that the parameter is obviously not just INITIATOR_. The regexp just couldn't recognize the rest.

Fortunately we have unique ID for any given Def family: The defName. All four lines I linked at the beginning can simply use defName instead of label or Label and all will be solved.

Please do consider changing it. Thanks.

alattalatta commented 3 years ago

Please note that I'm not the one in your Discord so it would be better if you may answer here directly. I asked them to relay my words but I'll write here as well for history keeping anyway.

"Fortunately we have unique ID for any given Def family: The defName" This is false. Initially we were using only defName. But then I found out there are many things that depend on label to be differentiated.

I suspect such "many things" are something like a trait and a skill sharing the same defName? But you can still specify the def family. As I said, unique ID for any given Def family, not the whole Def class.

INITIATOR_skill_*skillDefName*_level
INITIATOR_trait_*traitDefName*_level

Other than this I can't see why it can't be differentiated.

alattalatta commented 3 years ago

I do hope that you are aware of Thought_Memory#CurStageIndex so that you may write in a way that you can differentiate and branch by a thought's severity.

Close this if you'd like.

sergiodinapoli commented 3 years ago

Hi @alattalatta ! Thanks for your help. I just manage the XML part of SpeakUp.

Please consider this:

                <!-- ===SleptInBedroom=== -->
                    <li>r_logentry(INITIATOR_thoughtDefName==SleptInBedroom,tag=ReactToThought_Bedroom)->[thought_bedroom] [what_about_yours]</li>
                    <li>thought_bedroom(INITIATOR_thoughtLabel==awful bedroom)->My [swearword]room is [swearword_adverb][ugly] — but at least I don't have to share it.</li>
                    <li>thought_bedroom(INITIATOR_thoughtLabel==decent bedroom)->I sleep in a [proper] bedroom.</li>
                    <li>thought_bedroom(INITIATOR_thoughtLabel==slightly impressive bedroom)->I like my bedroom — it's a nice one.</li>
                    <li>thought_bedroom(INITIATOR_thoughtLabel==impressive bedroom)->I sleep in an impressive bedroom. It's great!</li>
                    <li>thought_bedroom(INITIATOR_thoughtLabel==very impressive bedroom)->I sleep in a very impressive bedroom. I just love it!</li>
                    <li>thought_bedroom(INITIATOR_thoughtLabel==extremely impressive bedroom)->I get to sleep in an extremely impressive bedroom! </li>
                    <li>thought_bedroom(INITIATOR_thoughtLabel==unbelievably impressive bedroom)->My bedroom is truly astounding!</li>
                    <li>thought_bedroom(INITIATOR_thoughtLabel==wondrously impressive bedroom)->Sleeping in my bedroom feels like heaven. Just wondrous!</li>

Here you can see there is one thoughtDefName==SleptInBedroom. I'd love to use just one, this one. But it actually branches into many labels. So it's not about skill and trait, there are unique (as far as I know). It's about other mechanisms.

About Thought_Memory#CurStageIndex, I don't know about C# details. But maybe @jptrrs can understand something.