Is there a (possibly "standard") way to get an ANSI C locale string
corresponding to particular LCID value? For example,
LCID: 1033 // English, United States, Default Code Page: 1252
LCID: 2048 // Russian, Russia, Default Code Page: 1251
should be converted to
"English_United States.1252"
"Russian_Russia.1251"
I managed to write the following code for this...
void
get_locale_string(TCHAR* locale, size_t size, LCID lcid =
LOCALE_SYSTEM_DEFAULT)
{
memset(locale, 0, size * sizeof(TCHAR));
int len = 0;
len = GetLocaleInfo(lcid, LOCALE_SENGLANGUAGE, locale, size - len);
if (!len)
{
return;
}
--len;
_tcsncat(locale + len, _T("_"), size - len);
++len;
int tmp = GetLocaleInfo(lcid, LOCALE_SENGCOUNTRY, locale + len, size
- len);
if (!tmp)
{
return;
}
len += tmp - 1;
_tcsncat(locale + len, _T("."), size - len);
++len;
GetLocaleInfo(lcid, LOCALE_IDEFAULTANSICODEPAGE, locale + len, size -
len);
}
Any thoughts?
--
Alex Shulgin
So, is there a problem with this code? What exactly are you aksing
about?
--
With best wishes,
Igor Tandetnik
With sufficient thrust, pigs fly just fine. However, this is not
necessarily a good idea. It is hard to be sure where they are going to
land, and it could be dangerous sitting under them as they fly
overhead. -- RFC 1925
I just seek for the most "natural" way to do this. If writing own
function is not that unnatural then it's just fine :-)
I do not know of any problem with this code, but may be people out
there see them...
In fact, the need for such a function was not out of curiosity. I
noticed that on the Russian version of WinXP (probably not only on XP
but on other versions too, but not tested; also see the stdlib version
below) a call to
std::fstream::open(char const*, std::ios::openmode)
fails subtly on filenames with Russian letters.
Debugging a bit showed that the implementation (VS 2005 C++ stdlib)
constructs a temporary buffer of wchar_t's to pass an Unicode string
with filename to the corresponding Unicode function overload which is
supposed to actually do the job. The implementation uses mbstowcs()
to obtain the Unicode filename which unfortunately fails to handle
Russian letters correctly since the locale was not properly setup.
Since I have never ever seen LOCALE environment variable to be set
under Windows I guessed that WinAPI locale mechanism might be used to
obtain "correct" locale string based on the default locale id.
Am I missing something about WinAPI and ANSI C locales?
--
Alex Shulgin
In my experience, CRT startup code sets up the locale to match the OS
user locale ( GetUserDefaultLCID ). It even goes as far as surfacing
manual overrides to number formats and such via localeconv - something
that can't be done by the program itself using public API only.
Are you saying this has not automatically happened in your application?
Are you calling setlocale yourself at any point?
> Since I have never ever seen LOCALE environment variable to be set
> under Windows I guessed that WinAPI locale mechanism might be used to
> obtain "correct" locale string based on the default locale id.
Well, you can look at setlocale implementation in CRT sources that ship
with MSVC. The interesting pieces are in getqloc.c. The code there does
essentially the inverse of what you are doing, using EnumSystemLocales
and GetLocaleInfo to find a locale where string representations of
language and country would match those passed to setlocale. So if your
goal is to deconstruct an LCID into a string that setlocale would
reconstruct back into the same LCID, I believe you are on the right
track. I'm not sure it's necessary to solve your real problem though.
Yes, in my experience (as well as in my expectations) it does not:
#include <stdio.h>
#include <locale.h>
int
main(void)
{
puts(setlocale(LC_ALL, NULL));
}
-- output --
C
Am I missing something again? ;-)
> Are you calling setlocale yourself at any point?
Sorry, I should have posted the complete example in the first place.
Here it is:
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <iostream>
#include <ostream>
#include <fstream>
#include <windows.h>
#include <tchar.h>
// grab the definition from the first post to make this work
void get_locale_string(TCHAR* locale, size_t size, LCID lcid =
LOCALE_SYSTEM_DEFAULT);
int
main(int argc, char* argv[])
{
TCHAR locale[64];
get_locale_string(locale, 64);
_tsetlocale(LC_ALL, locale);
if (argc != 2)
{
return EXIT_FAILURE;
}
char const* filename = argv[1];
#ifndef DONT_USE_STD_FSTREAM
std::ofstream file(filename);
if (!file.is_open())
{
std::cerr << "open: " << filename << ": " << strerror(errno) <<
std::endl;
return EXIT_FAILURE;
}
file << "test" << std::endl;
#else
FILE* file = fopen(filename, "w");
if (!file)
{
perror("fopen");
return EXIT_FAILURE;
}
fputs("test", file);
fclose(file);
#endif
}
The problem is that, while working perfectly with fopen(), it does not
work with std::ofstream unless I call setlocale explicitly as in
example.
> > Since I have never ever seen LOCALE environment variable to be set
> > under Windows I guessed that WinAPI locale mechanism might be used to
> > obtain "correct" locale string based on the default locale id.
>
> Well, you can look at setlocale implementation in CRT sources that ship
> with MSVC. The interesting pieces are in getqloc.c. The code there does
> essentially the inverse of what you are doing, using EnumSystemLocales
> and GetLocaleInfo to find a locale where string representations of
> language and country would match those passed to setlocale. So if your
> goal is to deconstruct an LCID into a string that setlocale would
> reconstruct back into the same LCID, I believe you are on the right
> track. I'm not sure it's necessary to solve your real problem though.
This is exactly the point which confuses me a lot :-) Looks like I
need to sync myself both APIs (C setlocale and WinAPI SetLocale) each
time I want to deal with locales...
Thanks!
--
Alex Shulgin
> puts(setlocale(LC_ALL, NULL));
> -- output --
> C
As you can see, the default setting is *always* "C"-locale at startup.
But please do not confuse the CRT-Local settings with the WinAPI-Locale
settings!
The WinAPI-Locale settings are set the the user-default settings (which
you can get/set with Get/SetLocaleInfo).
Greetings
Jochen
You are right. I figured it out. It's not CRT that synchronizes locales,
it's a library I'm using. And it's doing it with a simple call:
setlocale(LC_ALL, "");
The empty string for locale name is interpreted as "derive CRT locale
from GetUserDefaultLCID". It's even mentioned in setlocale docs: "If
locale points to an empty string, the locale is the
implementation-defined native environment."