How do format a date/time/number/currency in another locale? - windows

How do i format something for another locale in Windows?
For example, in managed C# code, i would try to render a DateTime using en-US locale with:
String s = DateTime.Now.ToString(CultureInfo.CreateSpecificCulture("en-US"));
TextRenderer.DrawText(
e.Graphics, s, SystemFonts.IconTitleFont,
new Point(16, 16), SystemColors.ControlText);
And that works fine when my computer's locale is en-US:
It even works fine when my computer's locale is de-DE:
But it completely falls apart when my computer's locale is ps-AF:
Note: My sample code is in .NET, but can also be native.
Update: Attempting to set System.Threading.Thread.CurrentThread.CurrentCulture to en-US before calling DrawText:
var oldCulture = System.Threading.Thread.CurrentThread.CurrentCulture;
System.Threading.Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US");
try
{
// String s = DateTime.Now.ToString(CultureInfo.CreateSpecificCulture("en-US"));
String s = DateTime.Now.ToString();
TextRenderer.DrawText(e.Graphics, s, SystemFonts.IconTitleFont, new Point(16, 16), SystemColors.ControlText);
}
finally
{
System.Threading.Thread.CurrentThread.CurrentCulture = oldCulture;
}
No help.
Nine, no help
Jack, no help
Eight, possible straight
King, possible flush
Ace, no help
Six, possible straight
Dave of love for the dealer
Ace bets.
Update Two:
From Michael Kaplan's blog entry:
Sometimes, GDI respects users (even if no one else does!)
GDI doesn't give a crap about formatting or really anything related to locales, with one single exception:
Digit Substitution
Any time you go to render text it will grab those digit substitution settings in the user locale (including the user override information) and use the info to decide how to display numbers.
And there is no way to override those settings at the level where GDI uses them.
i wonder how Chrome manages it. When i write digits here, in the stackoverflow question, Chrome renders them using latin digits:
0123456789
See:

What you are seeing is due to the digit substitution that occurs when your system's locale is ps-AF.
I believe that's OK -- Users of such a locale are used to seeing digits presented this way.
Normally the way this is done is slightly different, see here for example, but I don't actually think this should make any difference:
String s = DateTime.Now.ToString(new CultureInfo("en-US"));

An alternative is to set Thread.CurrentCulture to your desired locale.
I.e. do this:
Thread.CurrentCulture = new CultureInfo("en-US");
And you can then replace the first line of your code with this:
String s = DateTime.Now.ToString();
I am not quite sure, but I believe that this would solve the digit substitution issue as DrawText would now be based on the en-US culture, rather than ps-AF

Related

Keyboard Input with Webshim's Date Picker

I am using the webshims library to support older browsers better with more modern features. While the date picker works great with the mouse I seem to be having problems using it from the keyboard. The easiest way to see this is visit the demo page. I am using Firefox since it doesn't have date support.
Without making any modifications try to type in a date. I can enter numbers but I cannot enter a "/". If you enable the placeholder it even suggests the slash. I tried leaving out the separator or using "-" (which it lets me type) but when the form submits I get no value.
How are you supposed to enter a date via the keyboard?
For bonus points is it possible to allow the date picker to not enforce a format? I have backend code that can parse a wide variety of date formats. So they can use the date picker if they wish but if they type something in then whatever they type in is sent onto the server without modification.
Try this.This works for '/' format..
$.webshims.formcfg = {
en: {
dFormat: '/',
dateSigns: '/',
patterns: {
d: "mm/dd/yy"
}
}
};
webshims.activeLang('en');
It appears that there is an issue with the locale settings. From what I can tell, there is a form config attribute called dateSigns that gets set in the locale settings.
The solution for me was to go to file shims/combos/5.js and look for a chunk of code having dateSigns in it. I found the relevant one for US English around line 1750, which looks like this:
if(!formcfg['en-US']){
formcfg['en-US'] = $.extend(true, {}, formcfg.en, {
date: {firstDay: 0},
patterns: {d: "mm/dd/yy"},
dateSigns: '-',
dFormat: "/",
meridian: ['AM', 'PM']
});
}
I updated the dateSigns line to
dateSigns: '/',
It is a horrible hack, and there must be a way to set this as a configuration, or at least get the real locale settings to handle this. But I didn't manage to in the limited time I have available. But maybe this will help you. It works for me.

Print a code128 barcode starting with the character 'C'

I've written label printing software (Windows, WPF, C#, .net 4.5) that happily prints barcodes with a Datamax H-Class printer, with one exception, when printing a barcode that starts with the character C
When I attempt this, the barcode is truncated up until the first numeric character within it.
Lower case c works fine, but as some of our model codes do start with C, I need to find a way to work around this.
I guess there must be some sort of escape character that would allow this? But I've not managed to find it via Google.
I'm not 100% sure it's a code128 issue either, could it be related to the Datamax H-Class printer, the Datamax Windows C# SDK or possibly the code128 font we're using on the printer?
Sorry the details are so vague, any help or advice on what to check next would be very much appreciated.
Update.
Just in case this is of any use (I doubt it though sadly) the code I'm using to send barcodes to the printer (successfully in the case of all barcode strings not starting with C ) is as follows:
ParametersDPL paramDPL = new ParametersDPL();
paramDPL.Align = ParametersDPL.Alignment.Left;
paramDPL.Rotate = ParametersDPL.Rotation.Rotate_270;
paramDPL.IsUnicode = false;
paramDPL.TextEncoding = Encoding.ASCII;
paramDPL.WideBarWidth = 7;
paramDPL.NarrowBarWidth = 4;
paramDPL.SymbolHeight = 60;
//if the stockCode starts with 'C' the barcode will be truncated
docDPL.WriteBarCode("E", String.Format("{0} {1}", stockCode, serialNumber), COL_1, ROW_5, paramDPL);
The ParametersDPL object is from the Datamax C# SDK. The only possible problem I could see with the code is perhaps the setting of the IsUnicode or TextEncoding properties, but I've experimented with them quite a bit to no effect. None of the other properties on the ParametersDPL seemed like likely culprits either.
I'm unfamiliar with Datamax PCL, but the symptoms suggest that the "C" is being used to select subalphabet "C" of code128. It might be useful to try a stock code starting "A" or "ZB" and see whether the "A" or "B" disappears. If it does, then the first character may be being used to select a subalphabet ("A" is caps-only ASCII, "B" is no-controls ASCII.)
You'd then need to look very closely at Datamax PCL format - it may be that there's a (possibly opional) formatting character there, which makes it leading-character-sensitive. Perhaps forcing in a leading "B" would cure the problem.

Convert to E164 only if possible?

Can I determine if the user entered a phone number that can be safely formatted into E164?
For Germany, this requires that the user started his entry with a local area code. For example, 123456 may be a subscriber number in his city, but it cannot be formatted into E164, because we don't know his local area code. Then I would like to keep the entry as it is. In contrast, the input 089123456 is independent of the area code and could be formatted into E164, because we know he's from Germany and we could convert this into +4989123456.
You can simply convert your number into E164 using libphonenumber
and after conversion checks if both the strings are same or not. If they're same means a number can not be formatted, otherwise the number you'll get from library will be formatted in E164.
Here's how you can convert
PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
String formattedNumber = phoneUtil.format(inputNumber, PhoneNumberFormat.E164);
Finally compare formattedNumber with inputNumber
It looks as though you'll need to play with isValidNumber and isPossibleNumber for your case. format is certainly not guaranteed to give you something actually dialable, see the javadocs. This is suggested by the demo as well, where formatting is not displayed when isValidNumber is false.
I also am dealing with this FWIW. In the context of US numbers: The issue is I'd like to parse using isPossibleNumber in order to be as lenient as possible, and store the number in E164. However then we accept, e.g. +15551212. This string itself even passes isPossibleNumber despite clearly (I think) not being dialable anywhere.

convert case of wide characters, given the LCID (Visual C++)

I have some existing Visual C++ code where I need to add the conversion of wide character strings to upper or lower case.
I know there are pitfalls to this (such as the Turkish "I"), but most of these can be ironed-out if you know the language. Fortunately in this area of code I know the LCID value (locale ID) which I guess is the same as knowing the language.
As LCID is a Windows type, is there a Windows function that will convert wide strings to upper or lower case?
The C runtime function _towupper_l() sounds like it would be ideal but it takes a _locale_t parameter instead of LCID, so I guess it's unsuitable unless there is a completely reliable way of converting an LCID to a _locale_t.
The function you're searching for is called LCMapString and it is part of the Windows NLS APIs. The LCMAP_UPPERCASE flag maps characters to uppercase, while the LCMAP_LOWERCASE maps characters to lowercase.
For applications targeting Windows Vista and later, there is an Ex variant that works on locale names instead of identifiers, which are what Microsoft now says you should prefer to use.
In fact, in the CRT implementation provided with VS 2010 (and presumably other versions as well), functions such as _towupper_l ultimately end up calling LCMapString after they extract the locale ID (LCID) from the specified _locale_t.
If you're like me, and less familiar with the i8n APIs than you should be, you probably already know about the CharUpper, CharLower, CharUpperBuff, and CharLowerBuff family of functions. These have been the old standbys from the early days of Windows for altering the case of chars/strings, but as their documentation warns:
Note that CharXxx always maps uppercase I to lowercase I ("i"), even when the current language is Turkish or Azeri. If you need a function that is linguistically sensitive in this respect, call LCMapString.
What it neglects to mention is filled in by a couple of posts on Michael Kaplan's wonderful blog on internationalization issues: What does "linguistic casing" mean?, How best to alter case. The executive summary is that you achieve the same results as the CharXxx family of functions by calling LCMapString and not specifying the LCMAP_LINGUISTIC_CASING flag, whereas you can be linguistically sensitive by ensuring that you do specify the LCMAP_LINGUISTIC_CASING flag.
Sample code:
std::wstring test("Does my code pass the Turkey test?");
if (!LCMapStringW(lcid, /* your LCID, defined elsewhere */
LCMAP_UPPERCASE | LCMAP_LINGUISTIC_CASING,
test.c_str(), /* input string */
test.length(), /* length of input string */
&test[0], /* output buffer (can reuse input) */
test.length())) /* length of output buffer (same as input) */
{
// Uh-oh! Something went wrong in the call to LCMapString, so you need to
// handle the error somehow here.
// A good start is calling GetLastError to determine the error code.
}

How can I programmatically determine the current default codepage of Windows?

I have to convert the encoding of a string output of a VB6 application to a specific encoding.
The problem is, I don't know the encoding of the string, because of that:
According to the VB6 documentation when accessing certain API functions the internal Unicode strings are converted to ANSI strings using the default codepage of Windows.
Because of that, the encoding of the string output can be different on different systems, but I have to know it to perform the conversion.
How can I read the default codepage using the Win32 API or - if there's no other way - by reading the registry?
It could be even more succinct by using GetACP - the Win32 API call for returning the default code page! (Default code page is often called "ANSI")
int nCodePage = GetACP();
Also many API calls (such as MultiByteToWideChar) accept the constant value CP_ACP (zero) which always means "use the system code page". So you may not actually need to know the current code page, depending on what you want to do with it.
GetSystemDefaultLCID() gives you the system locale.
If the LCID is not enough and you truly need the codepage, use this code:
TCHAR szCodePage[10];
int cch= GetLocaleInfo(
GetSystemDefaultLCID(), // or any LCID you may be interested in
LOCALE_IDEFAULTANSICODEPAGE,
szCodePage,
countof(szCodePage));
nCodePage= cch>0 ? _ttoi(szCodePage) : 0;
That worked for me, thanks, but can be written more succinctly as:
UINT nCodePage = CP_ACP;
const int cch = ::GetLocaleInfo(LOCALE_SYSTEM_DEFAULT,
LOCALE_RETURN_NUMBER|LOCALE_IDEFAULTANSICODEPAGE,
(LPTSTR)&nCodePage, sizeof(nCodePage) / sizeof(_TCHAR) );

Resources