GIRA / PhysicalBits

A web-based programming environment for educational robotics that supports live coding and autonomy using a hybrid blocks/text programming language.
https://gira.github.io/PhysicalBits/
MIT License
18 stars 5 forks source link

Index localization messages with language independent keys #12

Closed kristiank closed 3 years ago

kristiank commented 4 years ago

I see it as a good practice to index the localization messages with language independent keys instead of the message in itself. This modification allows:

What is your feeling about this?

kristiank commented 4 years ago

Yes. That is a problem. However, I don't like having the code filled with language independent keywords. I like having the strings in some default language to give them context and make the code easier to read. I think we can make a compromise: treat the english language as any other but keep the current text indices. That means we would have the english strings twice but I don't think that is a problem. And this could allow us to use a language independent keyword when appropriate (like the simple 'on' and 'off') while keeping the code readable. Do you think that could work?

Yes, I definitely think this would work. It was more or less whta I had in mind, because it does, as you point out, give more context to the labels.

I will dig in to the i18n.js code and see what I can come up with.

RichoM commented 4 years ago

I just had an idea. What if we use strings more closely related to the underlying language syntax as language-independent keys?

For instance, for the forever block we could use something like: "forever { %2 }" and for the timer block we could use: "task %1 () %2 %3 / %4 { %5 }"

I haven't actually tested this, though, but I think if we do it like this maybe we could add a setting that just disables translation for the blocks and we could support this.

What do you think?

kristiank commented 4 years ago

In essence you mean an automatic way to derive blocks from Uzi syntax? This might be a pre-requisite to get proper support for bidirectional editing between blocks and code. But I might have misunderstood you?

I like this, but I wouldn't like to skip translations totally. Also, the bidirectionality you are aiming for between Uzi syntax and graphical blocks reduce very much the need for this kind of "exit strategy", because the users are free to switch between modes whenever anyway.

RichoM commented 4 years ago

In essence you mean an automatic way to derive blocks from Uzi syntax? This might be a pre-requisite to get proper support for bidirectional editing between blocks and code. But I might have misunderstood you?

No, that is way harder than it sounds and I'm not convinced it's really worth the effort. What I was suggesting is much more trivial. I did a small test with a couple of blocks, see here: d19dd25868e76401e7c73b809b34f022d1a39aa4

The idea would be to define each block's message string using the UziScript syntax instead of english. For instance, the if-else block definition would look like these:

Blockly.Blocks['conditional_full'] = {
  init: function() {
    let msg = i18n.translate("if %1 { \n %2 } else { \n %3 }"); // <-- Instead of english
    let inputFields = [
      () => this.appendValueInput("condition").setCheck("Boolean"),
      () => this.appendStatementInput("trueBranch").setCheck(null),
      () => this.appendStatementInput("falseBranch").setCheck(null)
    ];

    initBlock(this, msg, inputFields);

    this.setPreviousStatement(true, null);
    this.setNextStatement(true, null);
    this.setColour(210);
    this.setTooltip("");
    this.setHelpUrl("");
  }
};

We would then change the translations in order to make "if %1 { \n %2 } else { \n %3 }" translate to "if %1 then %2 else %3" (in english). So in practice it wouldn't change much except the strings won't be tied to a particular language.

But, since these are not arbitrary strings, if we want to support something like the "exit strategy" suggested by Blockly we can simply provide a setting that disables localization for all blocks, effectively making them look like this:

blockly-exit-strategy

Which is almost as writing UziScript code but without the hassle of remembering the syntax.

I like this, but I wouldn't like to skip translations totally. Also, the bidirectionality you are aiming for between Uzi syntax and graphical blocks reduce very much the need for this kind of "exit strategy", because the users are free to switch between modes whenever anyway.

You're probably right in that we might not need this feature if we can implement bidirectionality but anything that helps smooth the transition from blocks to text-based programming is worth exploring IMHO. I'm not sure this approach would work well with more "problematic" blocks, though. And also, it makes the blocks look very ugly. But I don't know, it seemed like an interesting idea...

kristiank commented 4 years ago

The more I think about this idea, the more I like it. I can start changing the index translations as I am working on the other translation messages.

kristiank commented 4 years ago

I have now tried going down the rabbit hole in #20. I think adding Uzi syntax as a translation is still a good thing to do, but it is not good enough for indexing the localizations. The lookup key for get_sonar_distance block gets simply %1 . %2 () which from a translators point of view is not telling very much context :-) Also, I assume Uzi is a context free grammar and thus there could be colliding lookup keys.

I will comment more on the actual changes I have done in #20 but will have this issue thread open for discussing language independent keys (maybe that is not practical, I don't have very much practice on GitHub collaboration).

My suggestion is to still use English as the lookup key while I add Uzi translations and we search for a better solution.

One thing I thought could add more context to the translations / localization messages is to use keywords in the inputFields instead of simple array numbering as we use now. What I mean is instead of %1 and %2 we could have %sonar_name and %distance_unit. The only real change in the code would be to change the inputFields to objects instead of arrays. What I like about this is that the field names would still be block-specific as the %1...%n are now.

Many thoughs, what are your feelings for all this?

RichoM commented 4 years ago

One thing I thought could add more context to the translations / localization messages is to use keywords in the inputFields instead of simple array numbering as we use now. What I mean is instead of %1 and %2 we could have %sonar_name and %distance_unit. The only real change in the code would be to change the inputFields to objects instead of arrays. What I like about this is that the field names would still be block-specific as the %1...%n are now.

That is a really good idea. I'm thinking now we will have a problem with a few blocks I added recently like the button_wait_for_long_action. I was lazy with the code generation and instead of adding a specific function for each time unit I made the conversion in the generated code. Like this:

physicalbits-buttons

I will have to change this so that it works like the sonar block. Do you see a problem with another block that could require changing the code generation?

kristiank commented 4 years ago

Yes, units is a weird topic which I don't really have a good understanding for. I think doing the same way as the sonar works is good. One thing I noticed with the sonar code is that the naming uses an underscore (e.g. distance_cm) but the delay block uses camel-casing instead (e.g. delayMs). Maybe always using the same schema?

One block that I haven't really figured out what it does is the elapsed_time block. It's output type is set to Number which indicates that it is some kind of counter. Although I seem to understand the block somehow more like a Boolean condition (stay false until an amount of time has elapsed, then turn true). Anyway, the elapsed_time contains the time unit, but it seem to be missing the value part? Just thinking out loud...

kristiank commented 4 years ago

... another way to tackle units could be to use converter-functions, like timeInSeconds(5) = 5000 and timeInMinutes(0.3) = 18000. But I don't know how intuitive this is pedagogically.

What my thoughts are suggesting here is to have separate blocks for Time and Distance. The blocks would take a Number value as input, but could also specify the unit of the value in the same way as the delay block is doing now. So the Time block could look something like [5 seconds] where the 5 is an input for Number and seconds is a dropdown for time units (ms, s, m, h). And the Distance block would look in a similar way [2 feet], where the dropdown for distance units could in fact allow other units than from the metric system. What do you think?

RichoM commented 4 years ago

One thing I noticed with the sonar code is that the naming uses an underscore (e.g. distance_cm) but the delay block uses camel-casing instead (e.g. delayMs). Maybe always using the same schema?

Yeah, you're right. I don't know how I managed to change styles without even noticing but I'll definitely fix it.

One block that I haven't really figured out what it does is the elapsed_time block. It's output type is set to Number which indicates that it is some kind of counter. Although I seem to understand the block somehow more like a Boolean condition (stay false until an amount of time has elapsed, then turn true). Anyway, the elapsed_time contains the time unit, but it seem to be missing the value part? Just thinking out loud...

No, the elapsed_time block translates directly to the millis() function in Arduino. You can see the primitive implementation here: https://github.com/GIRA/UziScript/blob/a70dea48045c0163040077578755b01d2a51055c/c%2B%2B/UziFirmware/VM.cpp#L633-L638

Maybe I should change the block label to make it more explicit? What do you suggest?

What my thoughts are suggesting here is to have separate blocks for Time and Distance. The blocks would take a Number value as input, but could also specify the unit of the value in the same way as the delay block is doing now. So the Time block could look something like [5 seconds] where the 5 is an input for Number and seconds is a dropdown for time units (ms, s, m, h). And the Distance block would look in a similar way [2 feet], where the dropdown for distance units could in fact allow other units than from the metric system. What do you think?

I like that idea. I think it could make some blocks simpler. But I'm not sure how much we would have to change to make this work. It seems like a big change, so I think I would rather wait until all the blocks are updated with their new language-independent labels before attempting to change this. In the mean time I would fix code generation to make these blocks work like the sonar.

kristiank commented 4 years ago

Maybe I should change the [elapsed_time] block label to make it more explicit? What do you suggest?

Since my work on the translation interface kind of moves the abstraction from the block name to the label used in the translations.js, I made an attempt to make it more clear/explicit in acaaf6d:

"elapsed time since bootup in %timeUnit"

What do you think?

kristiank commented 4 years ago

I took note of my ideas mentioned in this thread. They are too long running and maybe it would be a good idea to keep these issues more short-lived, e.g. should we close this thread? The feature of language independent keys has been implemented and use of reference labels in translation strings is ready to be merged in #22. Feel free to close this (or comment what you think could be prioritised work you want done from me :-))

RichoM commented 4 years ago

I was thinking of enabling a page in the wiki in order to keep all the long-running ideas in one place. What do you think?

kristiank commented 4 years ago

That sounds like a terrific idea!

RichoM commented 4 years ago

Great. I added an empty page here: https://github.com/GIRA/UziScript/wiki/Ideas I never used this feature before so I guess I'll learn on the go. I think you should have permissions to edit the page so feel free to write the ideas down there. I have some ideas that I will be adding later.