Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FR] Run-Time language selection #15064

Closed
marcio-ao opened this issue Aug 26, 2019 · 7 comments
Closed

[FR] Run-Time language selection #15064

marcio-ao opened this issue Aug 26, 2019 · 7 comments
Labels
T: Feature Request Features requested by users.

Comments

@marcio-ao
Copy link
Contributor

marcio-ao commented Aug 26, 2019

Currently Marlin only supports a compile-time selection of languages. We would like to expand our product line to European markets, but providing separately compiled firmware for each language isn't feasible.

Certain of our competitors have already implemented the capability to configure a language the first time the printer is powered on (and to allow it to be changed from the menu afterwards) and I would love to see this feature be standard in Marlin.

As far as I understand, the challenges in adding this feature to Marlin are the following:

  1. Changing the string table at run-time
  2. Changing the fonts and character mappers at run-time
  3. Keeping resource consumption unchanged for people who do not want this feature

For our purposes, only problem 1 would need to solved, since we merely want to be able switch among some European languages, all of which use the same font and character mapper.

Solving problem 1 is simply a matter of creating a table of pointers in PROGMEM, but doing so in a way that it does not cause a penalty for people who want to compile for a single language. It should also not complicate the Marlin code too much or make life harder for translators.

Proof-of-concept Code

With these requirements in mind, I tried coming up with a proof-of-concept Arduino sketch that accomplishes the design goal:

localization_test.ino.txt

I'm looking for some feedback on this. Actually implementing this in Marlin will take some work, in particular re-formatting all the language files, so I would like to make sure this scheme is acceptable to everyone before starting such a project!

Implementation Details

The language files are slightly modified. Rather than using the C preprocessor, there is now a separate namespace for each language:

namespace Language_ENGLISH {
  PROGMEM Language_Str MSG_LANGUAGE  = "English";
  PROGMEM Language_Str MSG_STRING_1  = "This is a string";
  PROGMEM Language_Str MSG_STRING_2  = "This is another string";
  PROGMEM Language_Str MSG_STRING_3  = "This is the penultimate string";
  PROGMEM Language_Str MSG_STRING_4  = "This is the last string";

  MAKE_LANGUAGE_STRINGS();
}

The MAKE_LANGUAGE_STRINGS macro builds a list of string pointers in PROGMEM. In order for it to know what strings to put in the table, there is the additional requirement of maintaining a list of strings identifiers (although this list will be very long, it only needs to be done once for the primary language):

#define LANGUAGE_STRINGS      MSG_LANGUAGE, MSG_STRING_1, MSG_STRING_2, MSG_STRING_3, MSG_STRING_4

Additional languages are defined in a similar manner, but using namespace can be used to "inherit" strings from the default language, just as #ifdefs are now used in "language_en.h". This allows missing strings to use the strings from the base language:

namespace Language_CATALANG {
  using namespace Language_ENGLISH;
  
  PROGMEM Language_Str MSG_LANGUAGE  = "Catalang";
  PROGMEM Language_Str MSG_STRING_2  = "Meow";
  PROGMEM Language_Str MSG_STRING_3  = "Meow Meow.";
  PROGMEM Language_Str MSG_STRING_4  = "Meow Purr.";

  MAKE_LANGUAGE_STRINGS();
}

As far as accessing individual strings from within Marlin, this is done using the GET_TEXT() macro, so readability is preserved. This macro is effectively a no-op in the single language compile, but involves a table look-up in the multi-language compile:

  Serial.println(GET_TEXT(MSG_STRING_1));

For end user, configuration of languages in "Configuration.h" is straight-forward:

// Define the default language for Marlin
#define DEFAULT_LANGUAGE Language_ENGLISH

// Define MULTI_LANGUAGE to allow change of language at run-time
//#define MULTI_LANGUAGE
#ifdef MULTI_LANGUAGE
  // Define a list of languages to support at run-time,
  // following the pattern:
  PROGMEM Language_List languages = {
    &Language_ENGLISH::strings,
    &Language_CANINE::strings,
    &Language_CATALANG::strings
  };
#endif

If they define MULTI_LANGUAGE, then the idea is that Marlin will present a menu at startup to allow them to select among those languages and save the selection to EEPROM. The user can choose any combination of languages that use the same font and font-mappers and as many as fit on their board's PROGMEM.

If MULTI_LANGUAGE is commented out, the language is determined by DEFAULT_LANGUAGE at compile-time. My initial testing shows the compiler is smart enough to inline all the strings so that when built without multiple-language support it should consume no more resources than what Marlin currently consumes.

UPDATE: Updated the code a bit so its even more readable.
UPDATE: A few more tweaks

@InsanityAutomation
Copy link
Contributor

FWIW Creality has done English / Chinese for awhile. Ive also seen French/English. With the space opening up for 32 bit boards, I'm not opposed to it, especially if passed through ExtUI where less of the strings are utilized the space difference isnt enormous. What youve spelled out here is much cleaner than what Creality did!

@marcio-ao
Copy link
Contributor Author

The PRUSA code is very complicated and I can't get my head around it, but it does look like they have the ability to read strings from a SPI EEPROM.

@InsanityAutomation
Copy link
Contributor

The PRUSA code is very complicated and I can't get my head around it, but it does look like they have the ability to read strings from a SPI EEPROM.

Thats because of the magic functions there! Gotta make sure the magic is valid....

Id have to see what theyre doing with the file out function there but it looks like theyre matching a data structure then dumping over it.

@marcio-ao
Copy link
Contributor Author

Here is another sketch that shows Marlin's current way of localizing strings. The important thing to notice is that it consumes the exact amount of memory as the sketch above when MULTI_LANGUAGE is disabled.

current_localization_test.ino.txt

@boelle boelle added the T: Feature Request Features requested by users. label Jan 19, 2020
@boelle boelle changed the title [FR, RFC, POC] Run-Time language selection [FR] Run-Time language selection Jan 19, 2020
@github-actions
Copy link

github-actions bot commented Jul 1, 2020

This issue is stale because it has been open 30 days with no activity. Remove stale label / comment or this will be closed in 5 days.

@rhapsodyv
Copy link
Member

I think this was solved by #20725. Right?

@github-actions
Copy link

github-actions bot commented May 1, 2021

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@github-actions github-actions bot locked and limited conversation to collaborators May 1, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
T: Feature Request Features requested by users.
Projects
None yet
Development

No branches or pull requests

5 participants