Q. What are the localizable elements in an RC file?
A. Too many pieces to answer in one line. But read the article to find out.
First I will create a small application that will cover as many localizable element types as possible.
I will start with a dialog-based MFC application (because the MFC resource files has some interesting extras that you will not find in other RC files). But once you can deal with this, regular resource files are easier.
So, here is the dialog (ugly, I know):

Now, let’s open the .rc as plain text and see what is inside.
I have tagged with cyan background the localizable elements, then I will take them one by one and explain.
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (U.S.) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif //_WIN32
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"#define _AFX_NO_SPLITTER_RESOURCES\r\n"
"#define _AFX_NO_OLE_RESOURCES\r\n"
"#define _AFX_NO_TRACKER_RESOURCES\r\n"
"#define _AFX_NO_PROPERTY_RESOURCES\r\n"
"\r\n"
"#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n"
"LANGUAGE 9, 1\r\n"
"#pragma code_page(1252)\r\n"
"#include ""res\\MFCLocalization.rc2"" // non-Microsoft Visual C++ edited resources\r\n"
"#include ""afxres.rc"" // Standard components\r\n"
"#endif\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDR_MAINFRAME ICON "res\\MFCLocalization.ico"
/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//
IDD_MFCLOCALIZATION_DIALOG DIALOGEX 0, 0, 326, 245
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION
EXSTYLE WS_EX_APPWINDOW
CAPTION "MFC Localization"
MENU IDR_MENU
FONT 8, "MS Shell Dlg", 0, 0, 0x1
BEGIN
DEFPUSHBUTTON "OK",IDOK,215,222,50,16
PUSHBUTTON "Cancel",IDCANCEL,269,222,50,16
LTEXT "Color:",IDC_STATIC,7,8,62,9
LTEXT "Numbers:",IDC_STATIC,7,24,62,9
COMBOBOX IDC_COMBO1,81,7,85,168,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP
COMBOBOX IDC_COMBO2,81,23,85,168,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP
CONTROL "Some checkbox control",IDC_CHECK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,42,100,10
CONTROL "Some radio control",IDC_RADIO,"Button",BS_AUTORADIOBUTTON,7,59,100,10
CONTROL "",IDC_SLIDER,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,7,76,100,15
CONTROL "",IDC_SPIN,"msctls_updown32",UDS_ARROWKEYS,97,98,10,14
CONTROL "",IDC_PROGRESS,"msctls_progress32",WS_BORDER,7,119,100,14
CONTROL "",IDC_HOTKEY,"msctls_hotkey32",WS_BORDER | WS_TABSTOP,7,140,100,14
CONTROL "",IDC_TREE,"SysTreeView32",WS_BORDER | WS_TABSTOP,223,113,96,102
LISTBOX IDC_LIST,119,113,96,102,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
CONTROL "",IDC_DATETIMEPICKER,"SysDateTimePick32",DTS_RIGHTALIGN | WS_TABSTOP,7,161,100,15
CONTROL "",IDC_MONTHCALENDAR,"SysMonthCal32",MCS_NOTODAY | WS_TABSTOP,179,7,140,100
CONTROL "",IDC_IPADDRESS,"SysIPAddress32",WS_TABSTOP,7,183,100,15
EDITTEXT IDC_EDIT,7,98,84,14,ES_AUTOHSCROLL
GROUPBOX "Group box",IDC_STATIC,119,39,48,68
END
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,1
PRODUCTVERSION 1,0,0,1
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x4L
FILETYPE 0x1L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904e4"
BEGIN
VALUE "CompanyName", "Mihai Nita"
VALUE "FileDescription", "Localization demo"
VALUE "FileVersion", "1.0.0.1"
VALUE "InternalName", "MFCLocalization.exe"
VALUE "LegalCopyright", "(c) Mihai Nita. All rights reserved."
VALUE "OriginalFilename", "MFCLocalization.exe"
VALUE "ProductName", "Localization demo"
VALUE "ProductVersion", "1.0.0.1"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1252
END
END
/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//
#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
IDD_MFCLOCALIZATION_DIALOG, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 319
TOPMARGIN, 7
BOTTOMMARGIN, 238
END
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Dialog Info
//
IDD_MFCLOCALIZATION_DIALOG DLGINIT
BEGIN
IDC_COMBO1, 0x403, 6, 0
0x6c62, 0x6361, 0x006b,
IDC_COMBO1, 0x403, 6, 0
0x6877, 0x7469, 0x0065,
IDC_COMBO1, 0x403, 4, 0
0x6572, 0x0064,
IDC_COMBO1, 0x403, 6, 0
0x7267, 0x6565, 0x006e,
IDC_COMBO1, 0x403, 5, 0
0x6c62, 0x6575, "\000"
IDC_COMBO1, 0x403, 5, 0
0x7963, 0x6e61, "\000"
IDC_COMBO1, 0x403, 8, 0
0x616d, 0x6567, 0x746e, 0x0061,
IDC_COMBO1, 0x403, 7, 0
0x6579, 0x6c6c, 0x776f, "\000"
IDC_COMBO2, 0x403, 4, 0
0x6e6f, 0x0065,
IDC_COMBO2, 0x403, 4, 0
0x7774, 0x006f,
IDC_COMBO2, 0x403, 6, 0
0x6874, 0x6572, 0x0065,
IDC_COMBO2, 0x403, 5, 0
0x6f66, 0x7275, "\000"
IDC_COMBO2, 0x403, 5, 0
0x6966, 0x6576, "\000"
IDC_COMBO2, 0x403, 4, 0
0x6973, 0x0078,
IDC_COMBO2, 0x403, 6, 0
0x6573, 0x6576, 0x006e,
IDC_COMBO2, 0x403, 6, 0
0x6965, 0x6867, 0x0074,
IDC_COMBO2, 0x403, 5, 0
0x696e, 0x656e, "\000"
IDC_COMBO2, 0x403, 4, 0
0x6574, 0x006e,
0
END
/////////////////////////////////////////////////////////////////////////////
//
// Accelerator
//
IDR_ACCELERATOR ACCELERATORS
BEGIN
"O", ID_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT
"F", ID_EDIT_FIND, VIRTKEY, CONTROL, NOINVERT
END
/////////////////////////////////////////////////////////////////////////////
//
// Menu
//
IDR_MENU MENU
BEGIN
POPUP "&File"
BEGIN
MENUITEM "&Open\tCtrl+O", ID_FILE_OPEN
MENUITEM SEPARATOR
MENUITEM "E&xit", ID_FILE_EXIT
END
POPUP "&Edit"
BEGIN
MENUITEM "&Find\tCtrl+F", ID_EDIT_FIND
END
END
/////////////////////////////////////////////////////////////////////////////
//
// String Table
//
STRINGTABLE
BEGIN
IDS_STRING1 "Some random string 1"
IDS_STRING2 "Some random string 2"
IDS_STRING3 "Some random string 3"
END
#endif // English (U.S.) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
#define _AFX_NO_SPLITTER_RESOURCES
#define _AFX_NO_OLE_RESOURCES
#define _AFX_NO_TRACKER_RESOURCES
#define _AFX_NO_PROPERTY_RESOURCES
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE 9, 1
#pragma code_page(1252)
#include "res\MFCLocalization.rc2" // non-Microsoft Visual C++ edited resources
#include "afxres.rc" // Standard components
#endif
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED
Now I will take each section and “translate” it to Japanese (which covers as many aspects as possible).
Language settings
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_JPN)
#ifdef _WIN32
LANGUAGE LANG_JAPANESE, SUBLANG_DEFAULT
#pragma code_page(932)
#endif //_WIN32
You can change this from the IDE (select each resource type, right-click and “Properties”, change “Language” to “Japanese”) or directly in the .rc file with a text editor. This will change all the elements in the section above.
But it will not change the things in the 3 TEXTINCLUDE. For that you will have to select the .rc file, right-click, select “Resource Includes…” and manually change the “Compile-time directives” section.
"#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_JPN)\r\n"
"LANGUAGE 0x11, 0x01\r\n"
"#pragma code_page(932)\r\n"
"#include ""res\\MFCLocalization.rc2"" // non-Microsoft Visual C++ edited resources\r\n"
"#include ""l.jpn\\afxres.rc"" // Standard components\r\n"
and
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_JPN)
LANGUAGE 0x11, 0x01
#pragma code_page(932)
#include "res\MFCLocalization.rc2" // non-Microsoft Visual C++ edited resources
#include "l.jpn\fxres.rc" // Standard components
#endif
The you can take AFX_TARG_? from the section produced by the IDE, same for the code page for the pragma code_page.
The language codes have to be numerical, so you can take the symbolic value generated by the IDE and look up the numerical value in WinNT.h ( LANG_JAPANESE is 0x11 and SUBLANG_DEFAULT is 0x01).
The interesting part is the afxres.rc file. This is the first MFC specific item. This file contains core error messages used by MFC (file access, memory, etc.) You can find the English version in <VSDir>\atlmfc\include\ and peek inside. Depending on the MFC features used by the application you might discover that more than one file is included ( afxctl.rc: some standard dialogs, afxdb.rc: database access errors, afxprint.rc: print dialog and errors, etc.). In Visual Studio 2005 there are 9 such files (7 MFC includes, named afx*.rc, and 2 ATL includes, named atl*.rc).
Microsoft provides (with Visual Studio) translated afx*.rc files for the following languages: Chinese Simplified ( l.chs), Chinese Traditional ( l.cht), German ( l.deu), Spanish ( l.esp), French ( l.fra), Italian ( l.ita), Japanese ( l.jpn) and Korean ( l.kor). If you don’t find them in <VSDir>\atlmfc\include\, check the Visual Studio install DVD and copy them from there.
If you need other languages you will have to translate them yourself. Sorry.
The comments are changed by the IDE.
// Japanese resources
...
#endif // Japanese resources
If you need to translate many files, the best option is to create a small Perl script to do the changes directly on the file (no need to waste time with the IDE).
Dialogs
The dialogs are the most visible part of the application, so it is very likely that you will not miss them.
For some languages you will have to change the font name and size.
For CCJK (Chinese Simplified and Traditional, Japanese, and Korean) you will need a bigger font.
You can find on the internet long debates about “MS Shell Dlg” vs “MS Shell Dlg 2” vs language specific font. But the only way to get reliable results no matter the OS language is to use the proper font for the localized resources.
See here for more in deep discussions on the topic:
- What about logical fonts?
- More problems with the Shell’s ‘ultimate font’ solutions
- Why the Windows Shell can’t provide the ultimate font solution for everyone (or even anyone!)
- Is this the Über-font post? No, but it is the teaser for it! (
So, here it is:
IDD_MFCLOCALIZATION_DIALOG DIALOGEX 0, 0, 326, 245
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION
EXSTYLE WS_EX_APPWINDOW
CAPTION "MFC の局在化"
MENU IDR_MENU
FONT 9, "MS ゴシック", 0, 0, 0x1
BEGIN
DEFPUSHBUTTON "OK",IDOK,215,222,50,16
PUSHBUTTON "取り消し",IDCANCEL,269,222,50,16
LTEXT "色:",IDC_STATIC,7,8,62,9
LTEXT "数:",IDC_STATIC,7,24,62,9
COMBOBOX IDC_COMBO1,81,7,85,168,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP
COMBOBOX IDC_COMBO2,81,23,85,168,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP
CONTROL "チェックボックス制御",IDC_CHECK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,42,100,10
CONTROL "無線制御",IDC_RADIO,"Button",BS_AUTORADIOBUTTON,7,59,100,10
CONTROL "",IDC_SLIDER,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,7,76,100,15
CONTROL "",IDC_SPIN,"msctls_updown32",UDS_ARROWKEYS,97,98,10,14
CONTROL "",IDC_PROGRESS,"msctls_progress32",WS_BORDER,7,119,100,14
CONTROL "",IDC_HOTKEY,"msctls_hotkey32",WS_BORDER | WS_TABSTOP,7,140,100,14
CONTROL "",IDC_TREE,"SysTreeView32",WS_BORDER | WS_TABSTOP,223,113,96,102
LISTBOX IDC_LIST,119,113,96,102,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
CONTROL "",IDC_DATETIMEPICKER,"SysDateTimePick32",DTS_RIGHTALIGN | WS_TABSTOP,7,161,100,15
CONTROL "",IDC_MONTHCALENDAR,"SysMonthCal32",MCS_NOTODAY | WS_TABSTOP,179,7,140,100
CONTROL "",IDC_IPADDRESS,"SysIPAddress32",WS_TABSTOP,7,183,100,15
EDITTEXT IDC_EDIT,7,98,84,14,ES_AUTOHSCROLL
GROUPBOX "グループ・ボックス",IDC_STATIC,119,39,48,68
END
Warning: do not use the above “translation”!!! It is done with BabelFish, and the quality is not what you want in your software, believe me!
Warning: be careful not to localize the class names! Some translation tools are dumb enough to consider all strings as localizable.
Obviously, you will have to resize the dialog, so the coordinates and flags will also change.
And make sure you also check for duplicated quick-keys (the IDE will help, try “Format” -> “Check Mnemonics”).
Version Info
One common practice is to not translate the original version info block, but to keep it in place and add a localized copy.
I do not have any compelling arguments pro or cons.
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "041104b0"
BEGIN
VALUE "CompanyName", "Mihai Nita"
VALUE "FileDescription", "局在化のデモ"
VALUE "FileVersion", "1.0.0.1"
VALUE "InternalName", "MFCLocalization.exe"
VALUE "LegalCopyright", "(c) Mihai Nita。複製権所有。"
VALUE "OriginalFilename", "MFCLocalization.exe"
VALUE "ProductName", "局在化のデモ"
VALUE "ProductVersion", "1.0.0.1"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x411, 1200
END
END
The non-string elements that change are the Windows LCID (0x409: English US to 0x411: Japanese) and the code pages (04e4/1252: Latin 1 to 04b0/1200: Unicode).
In fact, Unicode (1200) is a safe option for any language, so I would advise setting it for English too. I am waiting. Done? Ok.
It is easy to do this from the IDE, if you know where to look for it: open the VS_VERSION_INFO and find the “Block Header.” Right-click on it, select “Properties” and change “Language” to “Japanese” and “Code Page” to “Unicode”
DLGINIT
Yet another MFC specific element.
Although convenient to use for the programmers, it is a pain to translate (and there is also a bug associated with it, as you will see later on).
IDD_MFCLOCALIZATION_DIALOG DLGINIT
BEGIN
IDC_COMBO1, 0x403, 6, 0 0x6c62, 0x6361, 0x006b,
IDC_COMBO1, 0x403, 6, 0 0x6877, 0x7469, 0x0065,
IDC_COMBO1, 0x403, 4, 0 0x6572, 0x0064,
IDC_COMBO1, 0x403, 6, 0 0x7267, 0x6565, 0x006e,
IDC_COMBO1, 0x403, 5, 0 0x6c62, 0x6575, "\000"
IDC_COMBO1, 0x403, 5, 0 0x7963, 0x6e61, "\000"
IDC_COMBO1, 0x403, 8, 0 0x616d, 0x6567, 0x746e, 0x0061,
IDC_COMBO1, 0x403, 7, 0 0x6579, 0x6c6c, 0x776f, "\000"
IDC_COMBO2, 0x403, 4, 0 0x6e6f, 0x0065,
IDC_COMBO2, 0x403, 4, 0 0x7774, 0x006f,
IDC_COMBO2, 0x403, 6, 0 0x6874, 0x6572, 0x0065,
IDC_COMBO2, 0x403, 5, 0 0x6f66, 0x7275, "\000"
IDC_COMBO2, 0x403, 5, 0 0x6966, 0x6576, "\000"
IDC_COMBO2, 0x403, 4, 0 0x6973, 0x0078,
IDC_COMBO2, 0x403, 6, 0 0x6573, 0x6576, 0x006e,
IDC_COMBO2, 0x403, 6, 0 0x6965, 0x6867, 0x0074,
IDC_COMBO2, 0x403, 5, 0 0x696e, 0x656e, "\000"
IDC_COMBO2, 0x403, 4, 0 0x6574, 0x006e,
0
END
It contains the initial data for combo-boxes, and the easiest way is to do this in the IDE:

Right-click on the combo-box, select “Properties” and change the “Data” field (you know that, because you did put the English strings in there, didn’t you?
If you want to do it “the hard way” you can reverse-engineer the structures:
IDC_COMBO1, 0x403, 6, 0 0x6c62, 0x6361, 0x006b,
- IDC_COMBO1 is the control to which the data belongs
- 0x403 is the date type (combo-box text data)
- 6 is the string length
- 0x6c62, 0x6361, 0x006b: is the string, dumped as a collection of
WORDs, so the bytes are 62 6c 61 63 6b 00 (switched because we are little-endian). There are 6 of them, as we just learned before, and they should be interpreted according to the code page indicated by the from the pragma. This is “black\0”
A good trick for localization is to write a script that checks for 0x403 in DLGINIT blocks, so that you don’t forget about them. You can also extract the strings with a script. But it is safer to put them back from the IDE, dealing with hex-word-dumps of strings in various encodings might not be easy. And you have to resize every dialog anyway :-)
Warning: although in Visual Studio the Resource Editor supports Unicode (UTF-16) resource files, it does not handle properly the DLGINIT section. It will generate 0x3f3f (question marks), corrupting your translation.
Work-around: for languages having a code page you can use that code page instead of Unicode. A safer option is to not use the DLGINIT feature at all, but store the strings in the STRINGTABLE, then load and add them to the combo-box when you initialize the dialog (this is what MFC does anyway).
Accelerators
Not much to say here. Just make sure you don’t forget them.
IDR_ACCELERATOR ACCELERATORS
BEGIN
"O", ID_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT
"F", ID_EDIT_FIND, VIRTKEY, CONTROL, NOINVERT
END
The easiest way is to use the IDE. For Japanese these don’t usually change but for other languages they might. For instance Ctrl+F (“Find”) is usually changed to Ctrl+B (“Buscar”) for Spanish.
Make sure to check for duplicated entries.
Menus
The most obvious part are the strings. They are usually straight-forward to localize.
BEGIN
POPUP "ファイル(&F)"
BEGIN
MENUITEM "開いた(&O)\tCtrl+O", ID_FILE_OPEN
MENUITEM SEPARATOR
MENUITEM "出口(&X)", ID_FILE_EXIT
END
POPUP "編集しなさい(&E)"
BEGIN
MENUITEM "発見(&F)\tCtrl+F", ID_EDIT_FIND
END
END
One of the problems to be careful about: the quick-keys (note how Japanese keeps the same accelerators as the original, but they move at the end, in brackets, and capitalized).
The other thing to be careful is the descriptions of the accelerators. They should be translated (for instance in German “Ctrl” is translated as “Strg”).
They should also match the accelerators table (so in Spanish “Find\tCtrl+F” will have to change to “Buscar\tCtrl+B”).
And make sure you also check for duplicated quick-keys (the IDE will help, try “Format” -> “Check Mnemonics”).
Bug: this option was available in Visual Studio 6.0 both for menus and dialogs. But in newer VS versions it is present for dialogs only :-(
Strings tables
STRINGTABLE
BEGIN
IDS_STRING1 "任意ひも1"
IDS_STRING2 "任意ひも2"
IDS_STRING3 "任意ひも3"
END
This is the most obvious, simple, straightforward part.
I have nothing to teach you here, and at this point, after dialogs and menus, you should be a master anyway :-)
Other elements
You should also localize the other types of resources, if needed: icons, images, sounds, HTML files, etc.
In the end
This should do it for now.
What can I tell you more? Not sure, but here are some final notes:
1. Work with the ANSI code page, not with Unicode (change the system code page and reboot, see Setting the user and system locales).
- the Resource Editor still has problems with Unicode
- when you change the system code page, the default system UI font will change, affecting the dialog units and the resizing
2. If you set the LANGUAGE and pragma code_page properly, there is no need to use /l and /c when you call rc.exe ( LANGUAGE overrides /l and pragma code_page overrides /c)
3. Don’t forget to install the redistributable localized MFC libraries (see http://msdn2.microsoft.com/en-us/library/ms235264(vs.80).aspx).
4. You can download the code from here.