Opened 11 years ago

Last modified 4 months ago

#11594 confirmed defect

Fix wxLocale::GetSystemLanguage() and enhance it for Vista and later

Reported by: nielsm Owned by:
Priority: normal Milestone:
Component: wxMSW Version: 2.8.10
Keywords: wxLocale i18n MUI Cc: vaclavslavik
Blocked By: Blocking:
Patch: no

Description

Wanting to collect statistics on user preferred languages, I looked into what wx has to offer for detecting the system default language.

I discovered that the wxLocale::GetSystemLanguage() function has an incorrect implementation on Win32. It uses the Win32 API GetUserDefaultLCID(), which returns the user's preference for time and number formats, nothing related to user interface language.

The correct implementation should use GetSystemDefaultUILanguage() in general, and dynamically check for the availability of either the GetThreadUILanguage() function or the GetSystemPreferredUILanguages() function. Both of those were introduced with Windows Vista and return user preferences for MUI in Windows, while GetSystemDefaultUILanguage() exists since Windows 2000 at least, and returns the system install language.

I am currently working on an implementation using these functions in my software and will create a patch for wx once I have it working.

I have confirmed this in 2.8.10 and 2.9.0.

Change History (14)

comment:1 Changed 11 years ago by vaclavslavik

  • Cc vaclavslavik added

See also high-level overview.

The underlying problem is that we combine two things in wxLocale: the locale (for which GetUserDefaultLCID() is appropriate) and the language. This model is appropriate for Unix, but it doesn't match Vista+ or OS X.

For the record, Vista seems to follow OS X model: the user chooses ordered preference for languages and localized resources are used based on it, without respect to the locale (which, on OS X, is often just "C").

comment:2 Changed 11 years ago by vadz

  • Keywords i18n MUI added
  • Status changed from new to confirmed
  • Summary changed from wxLocale::GetSystemLanguage() uses wrong function on Win32 to Fix wxLocale::GetSystemLanguage() and enhance it for Vista and later

I think there are 2 issues here:

  1. The UI language may not correspond to the current locale. This is presumably rare but clearly may happen and should be fixed just as you propose.
  1. wxLocale only can return a single language and not an (ordered) list of languages. Ideally we'd add a new function to get array of all languages.

Patches for either/both of these would be appreciated, of course, but there is no need to solve both of them at the same time AFAICS.

Note that according to MSDN GetSystemDefaultUILanguage() is not present in Win 9x so we'd need to load it dynamically too unless we officially decide to drop support for those finally.

comment:3 Changed 22 months ago by kgschlosser

  • Cc drschlosser@… added

Since this problem is already reported I figured I would put some code in to be able to replicate the issue at hand. and give explicit instruction on how to replicate.

the code below is in Python and will require wxPython to be used. the issue actually lies in wxWidgets \src\common\intl.cpp line 789

What I have personally tested and does not work properly
Windows Versions: 7, 10
Python Versions: 2.7, 3.5
wxPython Versions: 3.0.2, 4.0.3

from __future__ import print_function
import wx
import ctypes
import locale
from ctypes.wintypes import (
    DWORD,
    WORD,
    INT,
    WCHAR,
    HANDLE
)

LOCALE_NAME_MAX_LENGTH = 85

LOCALE_INVARIANT = 0x007F
LOCALE_USER_DEFAULT =- 0x0400
LOCALE_SYSTEM_DEFAULT = 0x0800

KL_NAMELENGTH = 9

kernel32 = ctypes.windll.Kernel32
user32 = ctypes.windll.User32


if ctypes.sizeof(ctypes.c_long) == ctypes.sizeof(ctypes.c_void_p):
    ULONG_PTR = ctypes.c_ulong
elif ctypes.sizeof(ctypes.c_longlong) == ctypes.sizeof(ctypes.c_void_p):
    ULONG_PTR = ctypes.c_ulonglong
else:
    ULONG_PTR =ctypes. c_ulong


LCID = DWORD
LANGID = WORD
GEOTYPE = DWORD
HKL = HANDLE
DWORD_PTR = ULONG_PTR
PWSTR = ctypes.POINTER(WCHAR)


# LCID GetUserDefaultLCID();
GetUserDefaultLCID = kernel32.GetUserDefaultLCID
GetUserDefaultLCID.restype = LCID

user_default_lcid = GetUserDefaultLCID()
print('kernel32.GetUserDefaultLCID:', user_default_lcid)
if user_default_lcid in locale.windows_locale:
    print(
        'kernel32.GetUserDefaultLCID canonical name:',
        locale.windows_locale[user_default_lcid]
    )

else:
    print('kernel32.GetUserDefaultLCID canonical name: None')
# LCID GetSystemDefaultLCID();
GetSystemDefaultLCID = kernel32.GetSystemDefaultLCID
GetSystemDefaultLCID.restype = LCID

system_default_lcid = GetSystemDefaultLCID()
print()
print('kernel32.GetSystemDefaultLCID:', system_default_lcid)
if system_default_lcid in locale.windows_locale:
    print(
        'kernel32.GetSystemDefaultLCID canonical name:',
        locale.windows_locale[system_default_lcid]
    )

else:
    print('kernel32.GetSystemDefaultLCID canonical name: None')
# int GetUserDefaultLocaleName(
#   LPWSTR lpLocaleName,
#   int    cchLocaleName
# );
GetUserDefaultLocaleName = kernel32.GetUserDefaultLocaleName
GetUserDefaultLocaleName.restype = INT

user_locale_name = (WCHAR * LOCALE_NAME_MAX_LENGTH)()
GetUserDefaultLocaleName(
    ctypes.byref(user_locale_name),
    LOCALE_NAME_MAX_LENGTH
)

print()

print('kernel32.GetUserDefaultLocaleName:', user_locale_name.value)

# int GetSystemDefaultLocaleName(
#   LPWSTR lpLocaleName,
#   int    cchLocaleName
# );
GetSystemDefaultLocaleName = kernel32.GetSystemDefaultLocaleName
GetSystemDefaultLocaleName.restype = INT

system_locale_name = (WCHAR * LOCALE_NAME_MAX_LENGTH)()
GetSystemDefaultLocaleName(
    ctypes.byref(system_locale_name),
    LOCALE_NAME_MAX_LENGTH
)

print('kernel32.GetSystemDefaultLocaleName:', system_locale_name.value)

# LANGID GetUserDefaultUILanguage();
GetUserDefaultUILanguage = kernel32.GetUserDefaultUILanguage
GetUserDefaultUILanguage.restype = LANGID

user_default_ui_language = GetUserDefaultUILanguage()

print()
print('kernel32.GetUserDefaultUILanguage:', user_default_ui_language)
if user_default_ui_language in locale.windows_locale:
    print(
        'kernel32.GetUserDefaultUILanguage canonical name:',
        locale.windows_locale[user_default_ui_language]
    )

else:
    print('kernel32.GetUserDefaultUILanguage canonical name: None')


print()
# LANGID GetSystemDefaultUILanguage();
GetSystemDefaultUILanguage = kernel32.GetSystemDefaultUILanguage
GetSystemDefaultUILanguage.restype = LANGID

system_default_ui_language = GetSystemDefaultUILanguage()

print('kernel32.GetSystemDefaultUILanguage:', system_default_ui_language)

if system_default_ui_language in locale.windows_locale:
    print(
        'kernel32.GetSystemDefaultUILanguage canonical name:',
        locale.windows_locale[system_default_ui_language]
    )
else:
    print('kernel32.GetSystemDefaultUILanguage canonical name: None')


# LANGID GetUserDefaultLangID();
GetUserDefaultLangID = kernel32.GetUserDefaultLangID
GetUserDefaultLangID.restype = LANGID

user_default_langid = GetUserDefaultLangID()

print()
print('kernel32.GetUserDefaultLangID:', user_default_langid)

if user_default_langid in locale.windows_locale:
    print(
        'kernel32.GetUserDefaultLangID canonical name:',
        locale.windows_locale[user_default_langid]
    )

else:
    print('kernel32.GetUserDefaultLangID canonical name: None')

# LANGID GetSystemDefaultLangID();
GetSystemDefaultLangID = kernel32.GetSystemDefaultLangID
GetSystemDefaultLangID.restype = LANGID

system_default_langid = GetSystemDefaultLangID()
print()
print('kernel32.GetSystemDefaultLangID:', system_default_langid)

if system_default_langid in locale.windows_locale:
    print(
        'kernel32.GetSystemDefaultLangID canonical name:',
        locale.windows_locale[system_default_langid]
    )
else:
    print('kernel32.GetSystemDefaultLangID canonical name: None')


# HKL GetKeyboardLayout(
#   DWORD idThread
# );

GetKeyboardLayout = user32.GetKeyboardLayout
GetKeyboardLayout.restype = HKL

def LOWORD(l):
    return WORD(DWORD_PTR(l).value & 0xffff)


keyboard_layout = GetKeyboardLayout(DWORD(0))
keyboard_langid = LANGID(LOWORD(keyboard_layout).value)
print()
print('user32.GetKeyboardLayout:', keyboard_langid.value)
# BOOL GetKeyboardLayoutNameW(
#   LPWSTR pwszKLID
# );

if keyboard_langid.value in locale.windows_locale:
    print(
        'user32.GetKeyboardLayout canonical name:',
        locale.windows_locale[keyboard_langid.value]
    )

else:
    print('user32.GetKeyboardLayout canonical name: None')


wx_default = wx.Locale.GetSystemLanguage()
print('\n')
print('wx.Locale.GetSystemLanguage: ' + str(wx_default))

lang_info = wx.Locale.GetLanguageInfo(wx_default)

print('wx.LanguageInfo.LocaleName:', lang_info.GetLocaleName())
print('wx.LanguageInfo.CanonicalName:', lang_info.CanonicalName)
print('wx.LanguageInfo.Description:', lang_info.Description)
print('wx.LanguageInfo.Language:', lang_info.Language)

The code above leverages ctypes to access the Windows specific API calls to grab the language. Windows has a very complex language/locale setup. I believe this is due to decades of adding and patching.

In order to replicate the issue.
Start -> Control Panel -> Region and Language
Keyboards and Languages tab. Display langiage section. Install and enable
English (United States).
Formats tab. Format drop down. Select English (United States)
Administrative tab. (Now here is something very misleading) Change System Locale button. Set to English (United States). This may require a reboot.
Click the apply button. Run the script above the output will be

user default: 1033
system default: 1033
wx default: 60
user default canonical name: en_US
system default canonical name: en_US
wx default canonical name: en_US

Now repeat the steps above making only one change. Change the format
to Hebrew (Israel). Take note this is not changing the the displayed language
It is changing the language format which would be display of date/time, currency things of that nature. Which as best as i figure would be the locale.

rerun the script and the output will be

user default: 1033
system default: 1033
wx default: 100
user default canonical name: en_US
system default canonical name: en_US
wx default canonical name: he_IL

now if you notice the returned languages from the Windows API has not changed at all. But the language code from wxWidgets has. This is incorrect behavior

In Windows You have the ability to set the locale, displayed language, keyboard layout and finally the system language all separately. this adds quite a bit of complexity in deciding what to use in order to display the GUI components.

wxLocale.GetSystemLanguage is incorrect in 2 ways. first is it does not return the system language it actually returns the user locale. second is it should not return anything user based it should return the system language as the method name implies. But with respect to returning the default system language this is not something that should be needed. it is for use in non unicode applications as well as being the System "install" language, the defaulted language that windows uses when it is getting installed. It can be change via the Administrative tab and clicking on the Change System Locale button. and the button being labeled as such is very misleading because not only does setting that change the system locale. but it also changes the system language.

Now I specifically used the languages above for a reason. if you set wxLocale
using wx.Locale(wx.Locale.GetSystemLanguage()) and you have the format field set to Hebrew (Israel) the GUI gets completely flipped. including the written text gets displayed right to left. English (United States) is not a language that gets read from right to left and should not be displayed in this manner. But the whole GUI is actually flipped horizontally including the close and min/max buttons in the caption bar. Also if you open a dialog setting the current frame as a parent the displayed text in the dialog is in Hebrew and not the default user displayed language.

I can submit a patch for repairing the single method. But it appears that possibly other parts of wxWidgets might possibly use that method. I am unfamiliar with the code and it would take me a long while to locate any potential issues. As a quick and dirty band-aide I have build a cross reference from Windows LANDID's to wx.LANGUAGE_* codes. Use the ctypes Windows API call to GetUserDefaultUILanguage to get the LANGID feed it into the cross reference and use that output to set the language properly.

I believe the whole of wxLocale as well as wxLanguageInfo needs a code rewrite. I feel this should be done because when dealing with Windows the locale, displayed language, keyboard layout are separate things and using the locale to set the language is incorrect. and using the language to set the locale is also incorrect. I do not know how other operating systems function in respect to this.

The script above has the methods that can be used in determining the keyboard/language/locale.

Thank You for taking the time to go over this problem.

Version 0, edited 22 months ago by kgschlosser (next)

comment:4 Changed 22 months ago by vadz

Just to be clear: it's called GetSystemLanguage() but it is supposed to return the language that should be used for the user and has nothing to do with the system default language, whatever this is.

Also, I think we need to take a step back and think about what exactly is this function useful for. AFAIK it should only be used to determine the language to load translations for and from this point of view, it should indeed use ::GetThreadUILanguage(), just as mentioned in the original bug report, as it's more appropriate than ::GetUserDefaultLCID() if the two don't match (and in general they don't, although very often they do, of course).

And ideally we'd use ::GetThreadPreferredUILanguages() to allow selecting the first language for which we have translations available. Again, this is something mentioned in the original bug report and I still think it would be great to implement this.

I'm not sure if there are any other conclusions to draw from the comment:3. In particular I don't think we need to totally rewrite this code.

comment:6 Changed 22 months ago by vaclavslavik

Note that in the past 9 years, something changed: there's now wxTranslations API for loading translations in the way comment:4 and the original report describe. The flipping could be addressed by using RTL information from that, not the locale (and ideally deprecating anything language- related in wxLocale and keeping it for locale information only).

comment:7 Changed 22 months ago by vadz

To be honest, I'm not sure how is wxTranslations supposed to be used if you just want the application to appear in the user's language. It would be nice to modernize samples/internat to show it. Also, presumably, wxLocale still needs to be used if you want to show dates/times etc according to user's preferences, so I'm not completely convinced that language-dependent parts of wxLocale need to be deprecated as this would mean that you would have to explicitly use both it and wxTranslations in the most common case, instead of using just a single class (and forwarding the translations stuff to wxTranslations), which doesn't really look like a big gain for me.

Again, if anybody has any time to look into this (I really don't right now, sorry), I'd recommend starting by replacing ::GetUserDefaultLCID() with ::GetThreadUILanguage() and seeing what problems are left after doing this.

comment:8 Changed 22 months ago by vaclavslavik

To be honest, I'm not sure how is wxTranslations supposed to be used if you just want the application to appear in the user's language.

Parts of wxLocale are implemented in it, answering that… The API is mostly the same (except the addition of GetBestTranslation, which is used internally and which uses OS priority list to determine suitable language for given domain).

lso, presumably, wxLocale still needs to be used if you want to show dates/times etc according to user's preferences, so I'm not completely convinced that language-dependent parts

Locale formatting is not language-dependent. That's the whole point: while UI language handling doesn't map to locale well, has multiple prioritized languages and is disconnected from locale in modern OSes, locale is one, (user-)globally configured and handles formatting etc.

which doesn't really look like a big gain for me.

Clarity would be the gain - separating independent things (UI language, locale) into independent classes. It would fix the long-standing confusion of concepts in wx API that does lead to problems. The very flipping problem above illustrates this, it's a consequence of confusing locale with UI language even among core devs - not in the sense that we're unclear on the concepts (but many users are), but in the sense that there are all too handy, but wrong, tools at our disposal to do the wrong thing automatically. Clearly separating the two concepts would help avoid that.

comment:9 Changed 22 months ago by vadz

Vaclav, I don't really disagree with anything in the comment:8, but I'm not sure what exactly do you suggest should be done. AFAICS we still need to do what I wrote in the comment:7 and even though I don't have time to do anything anyhow right now, I still think it would be useful if you could please explain what (if anything) do you think should be done in more details. TIA!

comment:10 follow-up: Changed 6 months ago by kgschlosser

  • Cc drschlosser@… removed

I wanted to see if there has been advancement on this at all. There is another user that mentioned having the same issue as me with the RTL when changing the format (locale) in Windows. @robind posted a link to the issue on wxPython Phoenix.

I do not know how the locale and languages are handled in wxWidgets and if any of it is handled by wxPython. I know that there is an enumeration that is available in wxPython for languages/locals. I do not know the reason why this enumeration exists. I am going to make an assumption that there are constants of definitions in wxWidgets for the locals/languages. When I looked a while back I did not notice any code that queried Windows for things like decimal positions or format specifiers. I am also not sure why iso language_locale (en_US) identifiers are not the default mechanism used in setting the language and locale.

I did want to throw out there that Windows does have "virtual" LCID's and these virtual LCID's have been in Windows since at least Windows 7 and are probably used in Vista and maybe XP. Because these virtual LCID's were not commonly used issues never really arose from them. Windows 10 changed that. Almost all of the new languages/locales that were added to Windows 10 use these virtual LCID's. I cam across this when setting the locale in Python. Python pitches a fit when one of these language/locals are set. Language/locals that have a virtual LCID all share the same ID. 0x1000 I believe, so when using the Windows API it is really important to minimize using LCID's. The iso name is what should be used where ever available.

I wrote a helper module to address this issue in Python and also wxPython/wxWidgets. It is written in Python (ctypes) which can be ported to c code fairly easily. I mention this helper because it shows one approach to managing Windows languages and locals. It supports all languages/locals available from Windows XP to Windows 10 including all of the available language packs. Not all of these are supported by wxPython/wxWidgets but it does map the ones that are supported. On a clean Windows 7 x64 SP1 installation there are 123 locales and 196 languages and of those wxPython/wxWidgets has support for 127 possible locale/language combinations.

https://github.com/kdschlosser/pyWinLocale

The module also has all of the Windows API calls for getting the locale format specifiers for a given locale using the iso name. it also supports the 3 different iso name specifications for the country code.

I do want to tip my hat to the devs of wxWidgets for writing wxWidgets. It is one hell of a task to write a cross platform project like this. It is a huge amount of hard work that has taken place. I thank you guys/gals for the time and effort put in to make wxWidgets available to everyone.

I am not a c developer, I know enough to get into trouble. I do know Windows API, if there is any way I would be able to help let me know.

comment:11 Changed 6 months ago by kgschlosser

  • Cc drschlosser@… added

I wanted to mention that I am in agreement with Vaclav as far as how locals and languages should be handled.

This is my understanding of locals and languages work. please correct me if my thought process is incorrect on this. There is a heap of information on this exact topic and quite a bit of it is conflicting. But this is what I have come up with and how it should be done.

There has always been a large amount of fog when it comes to languages and locals in software. This is due to the thought that they are joined in some manner. the iso codes seem to strengthen this thought. They are not joined. They need to be able to be changed completely independent of one another. Windows has added to this thought process as well, and that has not helped what's so ever.

a locale simply defines the format specifiers, things like decimal positions, time formatting. currency formatting.

the language is exactly that. it is the displayed character set.

The only way the locale and language is linked together is by means of the code pages (character map) this is so that the proper characters can be gotten for the selected language when formatting the time, or currency so it can be displayed to the user correctly.

So because they should be handled independently of one another they should be coded as such. there should be a class for language and there should be a completely separate class for locale. the back end code would tie the 2 together for the purposes of making a call to the OS in order to get proper format specifiers for a given locale.

OS's have provided hard coded identifiers for common locale/language combinations. These identifiers have also added confusion. They seem to be thought of as what the OS supports. This is not true. any locale can be paired with any language. so if an application has support for 32 languages and 32 locals then there are 992 locale/language combinations the OS supports.

There should be a single function for convenience when setting the locale/language. This function would be passed the iso representation of a locale/language (en_US) and the function would construct the locale and language class instances and then set wxWidgets to use those instances at the default language and locale.

The user should be able to construct the instances them selves if wanted. Those classes should have methods available so a user is able to query the OS for information about if a language is RTL or not. or where the currency identifier gets placed for a given locale. and what the currency identifier is for a given language.

It would be nice to be able to set a widgets locale and language to be different from the default one. The application I work on has a translation dialog that is available to the user. It allows them to translate the application to any language they want. It would be great if the application was able to display 2 locals/language combinations at the same time.

I believe this is what Vaclav was thinking. I could be incorrect.

comment:12 in reply to: ↑ 10 ; follow-up: Changed 6 months ago by vadz

Replying to kgschlosser:

I wanted to see if there has been advancement on this at all.

No, sorry, I just don't have any bandwidth for this and, unfortunately, I don't think this is going to change any time soon. I can only repeat that I still think that the smallest fix that would, IMHO, be helpful is fixing GetSystemLanguage() as described in comment:4 and any PRs implementing this would be definitely welcome.

I'm still not sure what API changes exactly was Vaclav suggesting. AFAICS wxTranslations is fine as it is, but we don't have any good way to decide which language should be used for it and this probably needs to be added.

As for wxLocale, it seems to also be fine as far for locale (not language) functionality. The fact that it can also be used to load translations may not be ideal from API design purity point of view, but we're not going to remove it for the observable future because of compatibility constraints anyhow, so we still need to make this work as well as possible -- i.e. by using the user preferred language(s) when wxLocale is initialized using wxLANGUAGE_DEFAULT.

comment:13 in reply to: ↑ 12 Changed 4 months ago by kgschlosser

  • Cc drschlosser@… removed

Replying to vadz:

Replying to kgschlosser:

I wanted to see if there has been advancement on this at all.

No, sorry, I just don't have any bandwidth for this and, unfortunately, I don't think this is going to change any time soon. I can only repeat that I still think that the smallest fix that would, IMHO, be helpful is fixing GetSystemLanguage() as described in comment:4 and any PRs implementing this would be definitely welcome.

I'm still not sure what API changes exactly was Vaclav suggesting. AFAICS wxTranslations is fine as it is, but we don't have any good way to decide which language should be used for it and this probably needs to be added.

As for wxLocale, it seems to also be fine as far for locale (not language) functionality. The fact that it can also be used to load translations may not be ideal from API design purity point of view, but we're not going to remove it for the observable future because of compatibility constraints anyhow, so we still need to make this work as well as possible -- i.e. by using the user preferred language(s) when wxLocale is initialized using wxLANGUAGE_DEFAULT.

OK so when I run the following code.

locale = wx.Locale()
locale.Init(wx.LANGUAGE_PASHTO)

I get the error locale Pashuto is not supported by the OS.

Yet when I query Windows directly It tells me that a locale of AF and a language of ps is indeed supported by my OS. Further more why is it trying to set the locale to a language and not a country?

All of the information you see below is gotten from the OS.

Afghanistan - AF

english_name: Pashto
english_locale_name: Afghanistan

name: Pashto
locale_name: Afghanistan
label: Pashto (Afghanistan)
native_name: پښتو
native_locale_name: افغانستان
native_label: پښتو (افغانستان)
ansi codepage: 0
codepage: 1
Windows LCID: 0x0463
iso code: ps-AF

I have not been able to find a way to set the locale and language independent of one another. I am able to set the language and locale in windows to ps_US without an issue. how come I cannot get wxWidgets to accept it? Both the language and the locale are supported by wxWidgets But not together. which does not make any sense. the locale is used only for formatting and nothing more. It queries the language code page to get symbols that represent things like decimal points and dollar signs and that is the extent of what a locals involvement with a language should be. a language is NOT married to a locale but in wxWidgets it appears as tho they are.

The world is a small place compared to what it used to be. There are all kinds of people that speak all kinds of languages in all sorts of different countries. The way wxWidgets is currently set up it limits languages to specific countries. So users that speak Swahili that live in Germany and have Windows set to that language and locale are not going to be able to run an application that uses wxWidgets unless they set their Windows locale to a standard locale that has a language of Swahili. That is a completely bonkers concept to me.

Using wxLocale and wxLanguage should be as simple as.

wx.SetLocale(wx.Locale('DE'))
wx.SetLanguage(wx.Language('sw'))

Now I know that wxSetLocale does exist. Problem is that it requires a hell of alot of code in order to assemble the locale string in order to use it. and using wxLocale to do it does not work because of the LCID issue.

wxSetLocale should take a single parameter wxLocale. it should be able to build the locale string by calling a function that should exist wxGetLanguage
getting the ANSI code page from the language and also the language english name and getting the locale english name and build the string that is needed.

when wx.SetLanguage is called it will set the language and then call wxSetLocal(wxGetLocale()) to set the language locale and code page properly.

Do not rely on Windows LCID's as a way to identify a language/locale. Here is a list of language/locales that all have the exact same LCID.

https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-lcid/926e694f-1797-4418-a922-343d1c5e91a6

and then you have the virtual or dynamic LCID's
0x2000, 0x2400, 0x2800, 0x2C00, 0x3000, 0x3400, 0x3800, 0x3C00, 0x4000, 0x4400, 0x4800 and 0x4C00

On top of of that it ends up not working properly.

So using the LCID as the identifier when querying any of the Windows locale functions is going to either fail, or it is going to return incorrect information. there are alternate functions that take the iso code instead. and those are the functions that should be used.

here is an example to get the ansi code page for a locale.
This is what wxWidgets uses
GetLocaleInfo(lcid, LOCALE_IDEFAULTANSICODEPAGE, buffer, WXSIZEOF(buffer))

and here is how you do it without using an LCID.
GetLocaleInfoEx(LPCWSTR('en_us'), LOCALE_IDEFAULTANSICODEPAGE, buffer, WXSIZEOF(buffer))

the above will always work for getting a language code page so long as the language is installed into the system. the way it is done in wxWidgets will not because not all LCID's are unique.

So if I wanted to use the korean language with a local of United States the set_locale argument would be Korean_United States.949. no LCID needed to do this.

Now, this problem is coming up more and more because Windows 10 uses a whole bunch of duplicate LCID's and also dynamic LCID's. And also because of how small the World is getting on top of that and we are seeing more and more people using language/locales that are not pre defined.

I am not sure how else to go about explaining as to why the language/locale in wxWidgets does not work correctly on Windows. I have given about as much information in as much detail as I can possibly provide.

here are a few issues that were closed as invalid and in fact were not invalid.

#15257 - closed as invalid, and it is not invalid. 7 years ago
#15219 - closed as invalid, and it is not invalid. 7 years ago

I am sure there are other issues that have been reported that would stem from this problem. this should be the core of things like incorrectly formatted currency, dates, time, numbers... etc... or simply having crashes at startup or strange errors like the popup i got with a language name (and only the language name) being not supported by the OS as a locale..

please do some investigation and repair. I am not strong enough writing c/cpp code to make the changes that are need. Otherwise I would do it.

comment:14 Changed 4 months ago by vadz

Just to be perfectly clear: I simply don't have time to work on this. Yes, this is important, but so are 10000 other things and some of them are even more important than this one and even if they're not, I personally need them more and so I need to finish them first. I'd be very supportive of any improvements to wx API in this area, but I just can't, and won't, promise to do anything myself in the observable future.

For now my recommendation would be to use wxLocale for setting the locale only and using wxTranslations for loading the translations.

This is not going to work for LANG_PASHTO just because it's not present in misc/languages/langtabl.txt but this is trivial to update if anybody interested in adding it (and any other missing values) is motivated in doing it. But it should definitely work for LANG_GERMAN and set locale to de for it. And then you can use wxTranslations to load Swedish language messages.

Note: See TracTickets for help on using tickets.