GCC 编译器支持 gettext 的本地化,MinGW 也一样,只是可惜他们在内部实现时使用了绝对路径。这个绝对路径的前缀(prefix) 由编译时传递给 configure 的 --prefix 设定,这样就导致了 MinGW 只有在指定的位置上才能实现编译消息本地化。
我所做的就是让 GCC 在初始化 gettext 时使用相对路径,这样就能使 MinGW 在任何地方都能使用本地化的字符串了。包含文件搜索路径也同理。
通过查找 bindtextdomain 函数可以知道 gcc 对 gettext 的初始化在 gcc\intl.c 中完成。包含文件搜索路径定义在 incpath.c 中。
void
gcc_init_libintl (void)
{
#ifdef HAVE_LC_MESSAGES
setlocale (LC_CTYPE, "");
setlocale (LC_MESSAGES, "");
#else
setlocale (LC_ALL, "");
#endif
(void) bindtextdomain ("gcc", LOCALEDIR);
(void) textdomain ("gcc");
/* Opening quotation mark. */
open_quote = _("`");
/* Closing quotation mark. */
close_quote = _("'");
#if defined HAVE_LANGINFO_CODESET
locale_encoding = nl_langinfo (CODESET);
if (locale_encoding != NULL
&& (!strcasecmp (locale_encoding, "utf-8")
|| !strcasecmp (locale_encoding, "utf8")))
locale_utf8 = true;
#endif
if (!strcmp (open_quote, "`") && !strcmp (close_quote, "'"))
{
/* Untranslated quotes that it may be possible to replace with
U+2018 and U+2019; but otherwise use "'" instead of "`" as
opening quote. */
open_quote = "'";
#if defined HAVE_LANGINFO_CODESET
if (locale_utf8)
{
open_quote = "\xe2\x80\x98";
close_quote = "\xe2\x80\x99";
}
#endif
}
}
首先在开头加上:
#ifdef WIN32
#include <windows.h>
BOOL DirectoryExists(LPSTR lpszPath)
{
WIN32_FIND_DATA wfd;
BOOL bResult = FALSE;
HANDLE hFind = FindFirstFile(lpszPath, &wfd);
if ((hFind != INVALID_HANDLE_VALUE) && (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
bResult = TRUE;
}
FindClose(hFind);
return bResult;
}
#endif
其中 DirectoryExists 是用来判断路径是否存在的。
然后修改 (void) bindtextdomain ("gcc", LOCALEDIR); 为:
#ifndef WIN32
(void) bindtextdomain ("gcc", LOCALEDIR);
#else
DWORD dwSize = MAX_PATH + 20;
LPSTR lpszName = (LPSTR) xmalloc(dwSize);
DWORD dwRealSize = GetModuleFileNameA(NULL, lpszName, dwSize) + 1;
if (dwRealSize > dwSize)
{
lpszName = (LPSTR) xrealloc(lpszName, dwSize + 20);
GetModuleFileNameA(NULL, lpszName, dwRealSize + 20);
}
/* 去掉文件名 */
int l = strlen(lpszName);
while (l >= 0)
{
if (lpszName[l] == '\\')
break;
l--;
}
lpszName[l] = 0;
/* 去掉一层文件夹 */
l = strlen(lpszName);
while (l > 0)
{
if (lpszName[l] == '\\')
break;
l--;
}
/* 判断是否到根路径 */
if (lpszName[l] != '\\')
(void) bindtextdomain ("gcc", LOCALEDIR);
else
{
// 连接上 share\locale
lpszName[l + 1] = 0;
strcat(lpszName, "share\\locale");
if (DirectoryExists(lpszName))
(void) bindtextdomain ("gcc", lpszName);
else
(void) bindtextdomain ("gcc", LOCALEDIR);
}
free(lpszName);
#endif
这样重新编译后,无论 MinGW 位置在哪,它都能显示翻译后的消息了。