0

I am trying to define a nested map variable in a header file to use for key value lookup (or key, key value lookup since it is nested).

Apologies for being very new to C++ in general, let alone C++98. I have intermediate JavaScript experience, which might explain difficulties/habits.

I'm trying to insert spoken language translations into a UI using a nested map, something with a structure similar to this:

phrases["english"]["hello"] = "hi";
phrases["spanish"]["hello"] = "hola";

which will allow me to use phrases[selectedLanguage]["hello"] which will return(?) "hi" or "hola" depending on what selectedLanguage is set to.

This is so that a user can switch between languages while also allowing me to just change one translations.h file if/when needed.

I have a working version of the code which puts the map definitions within the .cpp code but I'd like to create something like a header file which defines my 'phrases' map variable so that I can separate language translations from the rest of the .cpp code.

My current working code looks like this:

UI.cpp:

void CScnMgr::InitScreens(){

  // selectedLanguage is defined 
  string selectedLanguage = "spanish";

  //phrases map is defined
  map <string, map <string, string> > phrases;
  phrases["english"]["hello"] = "hi";
  phrases["spanish"]["hello"] = "hola";

  // then later when i need to use either translation...
  phrases[selectedLanguage]["hello"];
}

This works, but I assume this is bad practice because it is creating this object every time the screens are initialized and for reasons I'm unfamiliar with. But I want to put my phrases map into a header file.

This is giving me errors:

translations.h:

#include <string>
#include <map>

int main(){
  map <string, map <string, string> > newPhrases;
  map <string, string> spanish;
  map <string, string> english;

  spanish["hello"] = "hola";
  english["hello"] = "hi";

  newPhrases["spanish"] = spanish;
  newPhrases["english"] = english;
  return 0;
}

UI.cpp:

#include "translations.h"


void CScnMgr::InitScreens(){
  int extern newPhrases;

// further down where I need to display to the UI...
  newPhrases[selectedLanguage]["hi"]

}

Errors:

UI.cpp: error: no match for 'operator[]' in 'newPhrases[selectedLanguage]'

I certainly don't understand why putting "int" in 'int extern newPhrases' passes compiling, but that's why it is there, I gave it the type of the main() return. I don't feel very comfortable doing that.

So I've defined selectedLanguage as "english" so I would expect C++ to handle that as newPhrases["english"], but it seems like newPhrases isn't defined as I expect it to be after importing it from translations.h

I'd appreciate a way to make this code work but I'd also appreciate reasons why this is the wrong way to go about this. Thanks in advance!

Schabry
  • 13
  • 3
  • Possible duplicate of [C++, Multilanguage/Localisation support](https://stackoverflow.com/questions/5432793/c-multilanguage-localisation-support) – recnac Apr 28 '19 at 00:55

1 Answers1

0

Let's try this step by step:

JavaScript to C++

That's quite a brave task :) I guess you chose the hard path going that way. It would have been easier the other way round. Well... it is what it is. Just let me say: C++ feels very different than JavaScript. I strongly recommend to do one of the myriads of tutorials and/or read a good book about it. There are plenty!

File Structure

Generally speaking, there should never be definitions in header files, only declarations. If you want to know more about this, google is your friend.

What you can do is having a declaration in the header file (using the keyword extern or by putting it into a class) and a definition in a (separate) cpp file. The linker will then find that definition and link the output together.

OOP

I strongly recommend to familiarize yourself with the OO concept. It will probably help you in the long run, and there might be more elegant solutions for your problem, but I won't go into detail here, see the other headings.

Analysis of your current code

  • This works, but I assume this is bad practice because it is creating this object every time the screens are initialized and for reasons I'm unfamiliar with. But I want to put my phrases map into a header file.

    The problem is that this object as you have it now is living on the stack and will be soon destroyed (overwritten) when you leave the function. So it won't work if you want to access phrases from a different function. You can read more about object lifetime here and a little more about how scope is connected to lifetime in the first link that popped up in google.

  • This is giving me errors:

    translations.h:

    #include <string>
    #include <map>
    
    int main(){
      map <string, map <string, string> > newPhrases;
      map <string, string> spanish;
      map <string, string> english;
    
      spanish["hello"] = "hola";
      english["hello"] = "hi";
    
      newPhrases["spanish"] = spanish;
      newPhrases["english"] = english;
      return 0;
    }
    

    Best practice is to not implement your functions in header files, but only declare them there and implement them in cpp files. For main(), you don't need a declaration. Just use a cpp file.

    The other thing is that you are creating newPhrases on the stack of main(), so newPhrases also only lives while main() is running. Probably not what you want.

  • UI.cpp:

    #include "translations.h"
    
    void CScnMgr::InitScreens(){
      int extern newPhrases;
    
    // further down where I need to display to the UI...
      newPhrases[selectedLanguage]["hi"]
    
    }
    

    Errors:

    UI.cpp: error: no match for 'operator[]' in 'newPhrases[selectedLanguage]'
    

    int extern newPhrases is just a declaration. It tells the compiler that there is something named newPhrases somewhere (but not here) and that it is of type int. Actually you would want to tell the compiler that this thing is of type map<string, map<string, string> >. Besides, extern declarations should not be inside functions. The error itself comes from your extern declaration. The compiler thinks that newPhrases is of type int, but something of type int doesn't have a square-bracket-operator (operator[]). But even if you fixed that it would not run, so I won't go into details how to get something possibly working. (See some suggestions and links in the next section)

The general approach about localization / internationalization / multi language support

The mindset about having the desire to abstract the language and splitting it from your code is good. The question is now how to solve it. A central idiom in programming in general is "not to reinvent the wheel".

Basically, I consider your question a duplicate to this one:
C++, Multilanguage/Localisation support

Another very similar topic that I found:
Bests practices for localized texts in C++ cross-platform applications?

Yet another one:
How to support multiple language in a Linux C/C++ program?

If you want to stick to your approach, have a look at this one (performance):
C++ map<std::string> vs map<char *> performance (I know, "again?")

In the posts mentioned above, there are many suggestions, also frameworks that can handle your problem pretty well. I also recommend to do your own research because some of the questions are rather old and there might be some new stuff already. Just ask your loyal friend

Michi
  • 681
  • 1
  • 7
  • 25
  • Thank you for the response! – Schabry May 03 '19 at 23:38
  • You mentioned that in my working code "This object as you have it now is living on the stack and will be soon destroyed (overwritten) when you leave the function" However, this function is actually always running during the entire lifecycle of the program. Is it therefore acceptable to define this map in that function and leave it as is? In the near future I will likely try to implement the first option in the answer from the duplicate question you mentioned with just .h & c file. I had difficulty googling solutions considering language = english/spanish or c++ Thanks! – Schabry May 03 '19 at 23:54
  • For the stack topic, check out https://stackoverflow.com/questions/12333062/when-should-a-class-be-allocated-on-the-stack-instead-of-the-heap – Michi May 05 '19 at 00:14