Search code examples
c++mfcmultilingual

Add a second language to a MFC project without using resource DLLs


I have a really little application made with MFC with german menus. Now I was asked to make an english version too. But the goal is to not use additional language DLLs. There should only be two .exe files (german and english) at the end. Do you know any step by step manual how to add a second resource file for the english dialogues (if needed) and how to use them in my C++ code? My idea is to use a preprocessor directive:

#ifdef APPLANG=EN
    m_wndRibbonBar.LoadFromResource(IDR_RIBBON_EN);
#else if
    m_wndRibbonBar.LoadFromResource(IDR_RIBBON);
#endif

Is it a good idea to do it this way? And what are the right steps to simply copy the existing resource files and modify them to English?

I have tried several things like making a new resource file and copy the original german menu to the new file. But than I get several errors like "Enter a legal resource ID".


Solution

  • You can embed multiple language resources in a single .exe file without having to use resource DLLs. There is no support for this use case by the Visual Studio resource editor, so resource scripts must be edited manually, using a text editor.

    To define the resources, use .rc2 files which the Visual Studio resource editor won't try to modify. Make sure to store the .rc2 files with Unicode (UTF-16 LE) encoding and always end them with a line break, otherwise the resource compiler will fail.

    Steps

    1. A project created through the MFC application wizard already includes an empty .rc2 file, which we can use as our "main" .rc2 file. Otherwise follow the documentation or create a new MFC application using the wizard to dissect how the .rc2 file is included.
    2. In the main .rc2 file add an #include for each language-specific .rc2 file:

      #include "lang_en.rc2"
      #include "lang_de.rc2"
      
      // Restore default language for resources included after current file
      LANGUAGE LANG_ENGLISH, SUBLANG_NEUTRAL
      
    3. Create the language-specific .rc2 files. Each file must start with LANGUAGE <LANGID>, <SUBLANGID> to indicate the language of the following resources:

      lang_en.rc2

      LANGUAGE LANG_ENGLISH, SUBLANG_NEUTRAL
      STRINGTABLE
      BEGIN
          IDS_STRING1 "Stack Overflow"
          IDS_STRING2 "Stack Overflow is a privately held website, the flagship site of the Stack Exchange Network, created in 2008 by Jeff Atwood and Joel Spolsky."
      END
      

      lang_de.rc2

      LANGUAGE LANG_GERMAN, SUBLANG_NEUTRAL
      STRINGTABLE
      BEGIN
          IDS_STRING1 "Stapelüberlauf"
          IDS_STRING2 "Stack Overflow (englisch für Stapelüberlauf) ist eine Internetplattform, auf der angemeldete Benutzer Fragen zum Thema Softwareentwicklung stellen können."
      END
      
    4. Build the executable and examine it in a resource editor to see if the resources are actually included for multiple languages. Instead of using a resource editor, you can also open the .exe file in Visual Studio to examine its resources. For instance, the demo .exe I build for this answer, looks like this, when opened in Visual Studio:

      Multi language string table

    In the code you can load resources as usual, Windows will automatically load the language resource that best matches the current user locale. If it doesn't find a match, it falls back to English.

    If you want to explicitly load a resource for a given language, you can do this by using the resource functions that have a language parameter, e. g. FindResourceEx().

    Including standard MFC resources

    Without further ado, standard MFC resources will be included only in the "main" language that is configured in the resource properties of the project.

    In the projects where I have used this approach, I had no need for the standard MFC resources. The following is just an untested idea how it could possibly be done.

    In each language-specific .rc2 file that is not the "main" language, add #includes for the MFC standard resources after the LANGUAGE line, e. g.:

    LANGUAGE LANG_GERMAN, SUBLANG_NEUTRAL
    
    #ifdef __AFXRES_RC__
        #undef __AFXRES_RC__    // To be able to include multiple language versions of afxres.rc
    #endif
    #include "l.deu\afxres.rc"  // Standard MFC resources
    
    STRINGTABLE
    BEGIN
        IDS_STRING1 "Stapelüberlauf"
        IDS_STRING2 "Stack Overflow (englisch für Stapelüberlauf) ist eine Internetplattform, auf der angemeldete Benutzer Fragen zum Thema Softwareentwicklung stellen können."
    END
    

    The #undef is there to circumvent the include guard of the standard MFC resource file, which normally prevents multiple includes. In our case this is OK, because the resources will end up in different language resource sections of the executable.