i18n Multi-Lingual Array

See the code in action on Tinkercad

/*
  This is my quick and dirty Tutorial/Demo of a "i18n Multi-Lingual 'array'"
  By using this method you can create easily customizable Multi-ligual sketches
  Created by Alain Martel
 */

#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     This section can be removed, I use it as a "safety" because I have unique settings in ENGLISH defines
#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      //Only required if your SET DEFAULT LANGUAGE SETTING is not 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);
    printProgStr ((const char *) pgm_read_word(&(menu1[lang][i]))); //Copies string from program and prints directly to LCD
    lcd.setCursor(0, 1);
    lcd.print(F("menu1["));
    lcd.print(lang);
    lcd.print(F("]["));
    lcd.print(i);
    lcd.print(F("]"));
    delay(1000);
    lcd.clear();
  }
 } 
}

int freeRam () {    //From https://playground.arduino.cc/Code/AvailableMemory/
  extern int __heap_start, *__brkval;
  int v;
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}

// Print a string from Program Memory directly to save RAM 
void printProgStr(const char * str) {
  char singleCharBuffer;
  if(!str) 
    return;
  while((singleCharBuffer = pgm_read_byte(str++)))
    lcd.print (singleCharBuffer);
}