Select Languages at Compile-time for dynamic-multi-lingual-array

I have been trying to figure out a memory efficient way of selecting languages (at compile-time) creating multi dimentional arrays, that can then be used to dynamically set the displayed language at run time.

The reason for this is to create multi-lingual sketches that are easy to modify by adding/removing the languages that are available. Once compiled the created array will allow the end user to dynamically choose the language that is going to be displayed or transmitted.

Good news I have a working sketch :slight_smile:

You can try it out at the following link on TinkerCAD:

Tinkercad "Compile-time-dynamic-multi-lingual-array"

The code is below:

#include <avr/pgmspace.h>        //Used to store "constant variables" in program space (in this case for messages displayed on LCD)

//Multi-Lingual display Start
#define ArrayCount(array) ((sizeof array / sizeof array[0]) - 1)
// For myArray[X][Y]
// X = (ArrayCount(menu2));
// Y = (ArrayCount(menu2[0]));

//Languages to include:
#define ENGLISH  
#define FRENCH            //Comment out any of these defines to exclude a language from the array
//#define SPANISH         //Uncomment to include in the array
#define PORTUGUES
#define GERMAN   

//Set Default Language
#ifndef ENGLISH            //This ensures the default language is set 
  #define ENGLISH
#endif

//Used to demonstrate changing the available menu items
#define CONFIGURATION  //Commenting this will remove "Settings" from the array

//Start putting Strings into Program Memory
#ifdef ENGLISH
  const static char langEN  [] PROGMEM = "English";
  const static char menu1en1[] PROGMEM = "Language";
  const static char menu1en2[] PROGMEM = "Game 1";
  const static char menu1en3[] PROGMEM = "Game 2";
  const static char menu1en4[] PROGMEM = "Game 3";
  const static char menu1en5[] PROGMEM = "Game 4";
  const static char menu1en6[] PROGMEM = "Game 5";
  #ifdef CONFIGURATION
    const static char menu1en7[] PROGMEM = "Settings";
    #define ENGLISH_M1   { langEN, menu1en1, menu1en2, menu1en3, menu1en4, menu1en5, menu1en6, menu1en7 },
    //The following line is only required in the default language
    #define M1_Elements 8    //This will set the number of elements in the array
  #else
    #define ENGLISH_M1   { langEN, menu1en1, menu1en2, menu1en3, menu1en4, menu1en5, menu1en6 },
    //The following line is only required in the default language
    #define M1_Elements 7    //One less element in the array "Settings" is unavailable
  #endif
#endif  

#ifdef FRENCH  
  const static char langFR  [] PROGMEM = "Francais";
  const static char menu1fr1[] PROGMEM = "Langue";
  const static char menu1fr2[] PROGMEM = "Jeux 1";
  const static char menu1fr3[] PROGMEM = "Jeux 2";
  const static char menu1fr4[] PROGMEM = "Jeux 3";
  const static char menu1fr5[] PROGMEM = "Jeux 4";
  const static char menu1fr6[] PROGMEM = "Jeux 5";  
  #ifdef CONFIGURATION
    const static char menu1fr7[] PROGMEM = "Parametres";
    #define FRENCH_M1    { langFR, menu1fr1, menu1fr2, menu1fr3, menu1fr4, menu1fr5, menu1fr6, menu1fr7 },
  #else
    #define FRENCH_M1    { langFR, menu1fr1, menu1fr2, menu1fr3, menu1fr4, menu1fr5, menu1fr6 },
  #endif
#endif 

#ifdef SPANISH
  const static char langES  [] PROGMEM = "Espanol";
  const static char menu1es1[] PROGMEM = "Idioma";
  const static char menu1es2[] PROGMEM = "Juego 1";
  const static char menu1es3[] PROGMEM = "Juego 2";
  const static char menu1es4[] PROGMEM = "Juego 3";
  const static char menu1es5[] PROGMEM = "Juego 4";
  const static char menu1es6[] PROGMEM = "Juego 5";
  #ifdef CONFIGURATION
    const static char menu1es7[] PROGMEM = "Ajustes";
    #define SPANISH_M1   { langES, menu1es1, menu1es2, menu1es3, menu1es4, menu1es5, menu1es6, menu1es7 },
  #else
    #define SPANISH_M1   { langES, menu1es1, menu1es2, menu1es3, menu1es4, menu1es5, menu1es6 },
  #endif
#endif

#ifdef PORTUGUES
  const static char langPT  [] PROGMEM = "Portugues";
  const static char menu1pt1[] PROGMEM = "Lingua";
  const static char menu1pt2[] PROGMEM = "Jogo 1";
  const static char menu1pt3[] PROGMEM = "Jogo 2";
  const static char menu1pt4[] PROGMEM = "Jogo 3";
  const static char menu1pt5[] PROGMEM = "Jogo 4";
  const static char menu1pt6[] PROGMEM = "Jogo 5";
  #ifdef CONFIGURATION
    const static char menu1pt7[] PROGMEM = "Definicoes";
    #define PORTUGUES_M1 { langPT, menu1pt1, menu1pt2, menu1pt3, menu1pt4, menu1pt5, menu1pt6, menu1pt7 },
  #else
    #define PORTUGUES_M1 { langPT, menu1pt1, menu1pt2, menu1pt3, menu1pt4, menu1pt5, menu1pt6 },
  #endif
#endif 

#ifdef GERMAN   
  const static char langDE  [] PROGMEM = "Deutsche";
  const static char menu1de1[] PROGMEM = "Sprache";
  const static char menu1de2[] PROGMEM = "Spiel 1";
  const static char menu1de3[] PROGMEM = "Spiel 2";
  const static char menu1de4[] PROGMEM = "Spiel 3";
  const static char menu1de5[] PROGMEM = "Spiel 4";
  const static char menu1de6[] PROGMEM = "Spiel 5"; 
  #ifdef CONFIGURATION
    const static char menu1de7[] PROGMEM = "Einstellungen";
    #define GERMAN_M1    { langDE, menu1de1, menu1de2, menu1de3, menu1de4, menu1de5, menu1de6, menu1de7 },
  #else
    #define GERMAN_M1    { langDE, menu1de1, menu1de2, menu1de3, menu1de4, menu1de5, menu1de6 },
  #endif
#endif 
//End putting Strings into Program Memory

//This was the easiest way I found to count the number of Languages selected
//Start Count
const static char* const langMenu [] PROGMEM = { 
  langEN,
  #ifdef FRENCH 
    langFR,
  #endif
  #ifdef SPANISH
    langES, 
  #endif
  #ifdef PORTUGUES
    langPT,
  #endif
  #ifdef GERMAN
    langDE, 
  #endif
};

const uint8_t numLang = ArrayCount(langMenu); //((sizeof(langMenu) / sizeof(langMenu[0])) - 1);
const uint8_t numLangElements = numLang + 1;
//End Count

const static char* const menu1[numLangElements][M1_Elements] PROGMEM = {
  ENGLISH_M1           //Places the defined ENGLISH_M1 array here
  #ifdef FRENCH        //If othe Languages have been defined they will be added to the array
    FRENCH_M1
  #endif
  #ifdef SPANISH
    SPANISH_M1
  #endif
  #ifdef PORTUGUES
    PORTUGUES_M1
  #endif 
  #ifdef GERMAN   
    GERMAN_M1 
  #endif    
};

char charBuffer[17];    //Required to read from program memory into a buffer for processing  

uint8_t lang = 0; 
//Multi-Lingual display End

#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

void setup() {
  lcd.begin(16, 2);
  lcd.print(F("Enabled "));
  lcd.print(numLangElements);
  lcd.print(F(" Langs"));
  lcd.setCursor(0, 1);
  lcd.print(freeRam());
  delay(5000);
  lcd.clear();
  
}

void loop() {
for (lang; lang <= numLang; lang++) { 
  for (int i = 0; i<= M1_Elements - 1; i++) {
    lcd.setCursor(0, 0);
    strcpy_P(charBuffer, (char*) pgm_read_word(&(menu1[lang][i]))); //Copies string from program memory into buffer
    lcd.print(charBuffer);        //Prints the buffer
    lcd.setCursor(0, 1);
    lcd.print(F("menu1["));
    lcd.print(lang);
    lcd.print(F("]["));
    lcd.print(i);
    lcd.print(F("]"));
    delay(1000);
    lcd.clear();
  }
 } 
}

Does anyone have an idea as to how I would go about making a library out of this?
The intent is to be able to setup each language lookup table in separate headers and remove some of the leg work by automating the process and making it easier for people to integrate into thier sketches.

Alain74Martel:
I have been trying to figure out a memory efficient way of dynamically (at compile-time) creating multi dimentional arrays.

If it's created at compile time, then it's NOT dynamic. Dynamic variables are created at RUN TIME.

The Dynamic was reference to being able to change the displayed language at run time.
I edited the title, and tried to explain a bit beter.
Sorry for the confussion.

#define ENGLISH

Why bother?

#ifdef ENGLISH

Why bother?

You have this:

#ifndef ENGLISH            //This ensures the default language is set 
  #define ENGLISH
#endif

Why bother?

#ifndef ENGLISH            //This ensures the default language is set
  #define ENGLISH
#endif

You can change the default to a different default language

Example:

#ifndef SPANISH            //This ensures the default language is set
  #define SPANISH         //Note the default language has changed
#endif

It is ment to be a "safety" during development, so you do not forget to include at lease 1 language.
This "safety measure" is handled at compile time, so if the default language was defined, it gets ignored.

Your code always defines ENGLISH, no matter what else was defined. There is no default.

I appreciate constructive critisism, really I do.
But come on, really? Especially with the number of posts you guys have.
gfvalvo - Grammar?
vaj4088 - If you don't want it take it out, I find it useful I have to switch between 2 "default" languages
MorganS - Did you read the comment to vaj4088? That is how you change the default

The code works, changing the default language works, enough said.

Now back to the initial question:

Does anyone have an idea as to how I would go about making a library out of this?

The intent is to be able to setup each language lookup table in separate headers and remove some of the leg work by automating the process and making it easier for people to integrate into thier sketches.

Any other remarks are unproductive.
If you do not have anything helpful to post, please don't.