Enhance wxUILocale and wxLocaleIdent

Many improvements and fixes to wxUILocale:

- Add wxUILocale method for retrieving wxLocaleIdent identifier,
  localized names, layout direction.
- Add wxLocaleIdent attributes, getter, and setter for
  platform-dependent tags under Windows: extension, sort order.
- Modify method wxLocaleIdent::FromTag to support not only BCP 47-like
  tags, but also platform-dependent syntax.
- Modify method wxLocaleIdent::GetTag to allow specifying the tag type.
- Update internat sample to better show using wxUILocale.
- Update German and French message catalogs for internat sample (German
  fully translated, French msgIds only).
- Introduced wxUILocaleImplStdC under Windows, because locale "en-US" is
  not equivalent to the C locale.
- Adjust wxLocale class to restore previous wxUILocale in the
  destructor.
- Implement wxLocale::GetInfo method through wxUILocale methods.
- Removed LCID dependency in wxLocale.
- Move the implementation of some static wxUILocale methods from
  intl.cpp to uilocale.cpp.

Co-authored-by: Vadim Zeitlin <vadim@wxwidgets.org>

Closes #2615.
This commit is contained in:
utelle
2022-03-28 01:11:40 +02:00
committed by Vadim Zeitlin
parent 891cbb1e0e
commit 6d6e5cde21
17 changed files with 2015 additions and 673 deletions

View File

@@ -270,6 +270,7 @@ private:
m_strShort; // short name for the locale m_strShort; // short name for the locale
int m_language; // this locale wxLanguage value int m_language; // this locale wxLanguage value
wxString m_oldUILocale; // previous wxUILocale name
const char *m_pszOldLocale; // previous locale from setlocale() const char *m_pszOldLocale; // previous locale from setlocale()
wxLocale *m_pOldLocale; // previous wxLocale wxLocale *m_pOldLocale; // previous wxLocale
#ifdef __WIN32__ #ifdef __WIN32__

View File

@@ -32,6 +32,31 @@ enum wxLayoutDirection
// wxLocaleCategory: the category of locale settings // wxLocaleCategory: the category of locale settings
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
enum wxLocaleTagType
{
// Default (tag as given or else same as wxLOCALE_TAGTYPE_SYSTEM)
wxLOCALE_TAGTYPE_DEFAULT,
// Default type of the system (platform-dependent)
wxLOCALE_TAGTYPE_SYSTEM,
// BCP47-like type: <language>[-<script>][-<region>][-<modifier>]
wxLOCALE_TAGTYPE_BCP47,
// macOS type: <language>[-<script>][_<region>]
wxLOCALE_TAGTYPE_MACOS,
// POSIX type: <language>_<region>[.<charset>][@{<scriptalias>|<modifier>}]
wxLOCALE_TAGTYPE_POSIX,
// Windows type: <language>[-<script>][-<region>][-<extension>][_<sortorder>]
wxLOCALE_TAGTYPE_WINDOWS
};
// ----------------------------------------------------------------------------
// wxLocaleCategory: the category of locale settings
// ----------------------------------------------------------------------------
enum wxLocaleCategory enum wxLocaleCategory
{ {
// (any) numbers // (any) numbers
@@ -74,6 +99,27 @@ enum wxLocaleInfo
}; };
// ----------------------------------------------------------------------------
// wxLocaleName: the items understood by wxLocale::GetLocalizedName()
// ----------------------------------------------------------------------------
enum wxLocaleName
{
wxLOCALE_NAME_LOCALE,
wxLOCALE_NAME_LANGUAGE,
wxLOCALE_NAME_COUNTRY
};
// ----------------------------------------------------------------------------
// wxLocaleForm: the forms of names understood by wxLocale::GetLocalizedName()
// ----------------------------------------------------------------------------
enum wxLocaleForm
{
wxLOCALE_FORM_NATIVE,
wxLOCALE_FORM_ENGLISH
};
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// wxLanguageInfo: encapsulates wxLanguage to OS native lang.desc. // wxLanguageInfo: encapsulates wxLanguage to OS native lang.desc.
// translation information // translation information

View File

@@ -63,6 +63,11 @@ public:
// work, so it just falls back on CreateStdC() if it fails to create it. // work, so it just falls back on CreateStdC() if it fails to create it.
static wxUILocaleImpl* CreateForLanguage(const wxLanguageInfo& info); static wxUILocaleImpl* CreateForLanguage(const wxLanguageInfo& info);
// This function retrieves a list of preferred UI languages.
// The list is in the order of preference, if it has more than one entry.
// The entries contain platform-dependent identifiers.
static wxVector<wxString> GetPreferredUILanguages();
// Use this locale in the UI. // Use this locale in the UI.
// //
// This is not implemented for all platforms, notably not for Mac where the // This is not implemented for all platforms, notably not for Mac where the
@@ -72,7 +77,10 @@ public:
// Functions corresponding to wxUILocale ones. // Functions corresponding to wxUILocale ones.
virtual wxString GetName() const = 0; virtual wxString GetName() const = 0;
virtual wxLocaleIdent GetLocaleId() const = 0;
virtual wxString GetInfo(wxLocaleInfo index, wxLocaleCategory cat) const = 0; virtual wxString GetInfo(wxLocaleInfo index, wxLocaleCategory cat) const = 0;
virtual wxString GetLocalizedName(wxLocaleName name, wxLocaleForm form) const = 0;
virtual wxLayoutDirection GetLayoutDirection() const = 0;
virtual int CompareStrings(const wxString& lhs, const wxString& rhs, virtual int CompareStrings(const wxString& lhs, const wxString& rhs,
int flags) const = 0; int flags) const = 0;

View File

@@ -16,6 +16,7 @@
#include "wx/localedefs.h" #include "wx/localedefs.h"
#include "wx/string.h" #include "wx/string.h"
#include "wx/vector.h"
class wxUILocaleImpl; class wxUILocaleImpl;
@@ -56,12 +57,20 @@ public:
// Set modifier (only supported under Unix) // Set modifier (only supported under Unix)
wxLocaleIdent& Modifier(const wxString& modifier); wxLocaleIdent& Modifier(const wxString& modifier);
// Set extension (only supported under Windows)
wxLocaleIdent& Extension(const wxString& extension);
// Set sort order (only supported under Windows)
wxLocaleIdent& SortOrder(const wxString& sortorder);
// Accessors for the individual fields. // Accessors for the individual fields.
const wxString& GetLanguage() const { return m_language; } const wxString& GetLanguage() const { return m_language; }
const wxString& GetRegion() const { return m_region; } const wxString& GetRegion() const { return m_region; }
const wxString& GetScript() const { return m_script; } const wxString& GetScript() const { return m_script; }
const wxString& GetCharset() const { return m_charset; } const wxString& GetCharset() const { return m_charset; }
const wxString& GetModifier() const { return m_modifier; } const wxString& GetModifier() const { return m_modifier; }
const wxString& GetExtension() const { return m_extension; }
const wxString& GetSortorder() const { return m_sortorder; }
// Construct platform dependent name // Construct platform dependent name
wxString GetName() const; wxString GetName() const;
@@ -69,7 +78,7 @@ public:
// Get the language tag: for the objects created with FromTag() returns the // Get the language tag: for the objects created with FromTag() returns the
// string passed to it directly, otherwise reconstructs this string from // string passed to it directly, otherwise reconstructs this string from
// the components. // the components.
wxString GetTag() const; wxString GetTag(wxLocaleTagType tagType = wxLOCALE_TAGTYPE_DEFAULT) const;
// Empty locale identifier is invalid. at least Language() must be called. // Empty locale identifier is invalid. at least Language() must be called.
bool IsEmpty() const bool IsEmpty() const
@@ -85,6 +94,8 @@ private:
wxString m_script; wxString m_script;
wxString m_charset; wxString m_charset;
wxString m_modifier; wxString m_modifier;
wxString m_extension;
wxString m_sortorder;
}; };
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@@ -97,11 +108,11 @@ public:
// Configure the UI to use the default user locale. // Configure the UI to use the default user locale.
static bool UseDefault(); static bool UseDefault();
// Use the locale corresponding to the given language. // Use the locale corresponding to the given POSIX locale, e.g. "de_DE.UTF-8".
// //
// This is a compatibility function used by wxWidgets itself, don't use it // This is a compatibility function used by wxWidgets itself, don't use it
// in the new code. // in the new code.
static bool UseLanguage(const wxLanguageInfo& info); static bool UseLocaleName(const wxString& localeName);
// Get the object corresponding to the currently used locale. // Get the object corresponding to the currently used locale.
static const wxUILocale& GetCurrent(); static const wxUILocale& GetCurrent();
@@ -126,10 +137,19 @@ public:
// Get the platform-dependent name of the current locale. // Get the platform-dependent name of the current locale.
wxString GetName() const; wxString GetName() const;
// Get the locale id from which the current locale was instantiated.
wxLocaleIdent GetLocaleId() const;
// Query the locale for the specified information. // Query the locale for the specified information.
wxString GetInfo(wxLocaleInfo index, wxString GetInfo(wxLocaleInfo index,
wxLocaleCategory cat = wxLOCALE_CAT_DEFAULT) const; wxLocaleCategory cat = wxLOCALE_CAT_DEFAULT) const;
// Query the locale for the specified localized name.
wxString GetLocalizedName(wxLocaleName name, wxLocaleForm form) const;
// Query the layout direction of the current locale.
wxLayoutDirection GetLayoutDirection() const;
// Compares two strings in the order defined by this locale. // Compares two strings in the order defined by this locale.
int CompareStrings(const wxString& lhs, const wxString& rhs, int CompareStrings(const wxString& lhs, const wxString& rhs,
int flags = wxCompare_CaseSensitive) const; int flags = wxCompare_CaseSensitive) const;
@@ -142,6 +162,10 @@ public:
// Return wxLANGUAGE_UNKNOWN if language-guessing algorithm failed // Return wxLANGUAGE_UNKNOWN if language-guessing algorithm failed
static int GetSystemLanguage(); static int GetSystemLanguage();
// Try to retrieve a list of user's (or OS's) preferred UI languages.
// Return empty list if language-guessing algorithm failed
static wxVector<wxString> GetPreferredUILanguages();
// Retrieve the language info struct for the given language // Retrieve the language info struct for the given language
// //
// Returns NULL if no info found, pointer must *not* be deleted by caller // Returns NULL if no info found, pointer must *not* be deleted by caller
@@ -162,6 +186,13 @@ public:
// Returns NULL if no info found, pointer must *not* be deleted by caller // Returns NULL if no info found, pointer must *not* be deleted by caller
static const wxLanguageInfo* FindLanguageInfo(const wxString& locale); static const wxLanguageInfo* FindLanguageInfo(const wxString& locale);
// Find the language for the given locale string which may be either a
// canonical ISO 2 letter language code ("xx"), a language code followed by
// the country code ("xx_XX") or a Windows full language name ("Xxxxx...")
//
// Returns NULL if no info found, pointer must *not* be deleted by caller
static const wxLanguageInfo* FindLanguageInfo(const wxLocaleIdent& locId);
// Add custom language to the list of known languages. // Add custom language to the list of known languages.
// Notes: 1) wxLanguageInfo contains platform-specific data // Notes: 1) wxLanguageInfo contains platform-specific data
// 2) must be called before Init to have effect // 2) must be called before Init to have effect

View File

@@ -90,6 +90,34 @@ struct wxLanguageInfo
}; };
/**
The type of a locale tag.
@see wxLocaleIdent::GetTag()
@since 3.1.6
*/
enum wxLocaleTagType
{
/// Default (tag as given or else same as wxLOCALE_TAGTYPE_SYSTEM)
wxLOCALE_TAGTYPE_DEFAULT,
/// Default type of the system (platform-dependent).
wxLOCALE_TAGTYPE_SYSTEM,
/// BCP47-like type: <language>[-<script>][-<region>][-<modifier>].
wxLOCALE_TAGTYPE_BCP47,
/// macOS type: <language>[-<script>][_<region>].
wxLOCALE_TAGTYPE_MACOS,
/// POSIX type: <language>_<region>[.<charset>][@{<scriptalias>|<modifier>}].
wxLOCALE_TAGTYPE_POSIX,
/// Windows type: <language>[-<script>][-<region>][-<extension>][_<sortorder>].
wxLOCALE_TAGTYPE_WINDOWS
};
/** /**
The category of locale settings. The category of locale settings.
@@ -192,6 +220,50 @@ enum wxLocaleInfo
}; };
/**
The values understood by wxUILocale::GetLocalizedName().
The corresponding strings can be used to display the information in the UI.
@since 3.1.6
@see wxUILocale::GetLocalizedName()
*/
enum wxLocaleName
{
/// Display name of a locale.
wxLOCALE_NAME_LOCALE,
/// Display name of the language of a locale.
wxLOCALE_NAME_LANGUAGE,
/// Display name of the country/region of a locale.
wxLOCALE_NAME_COUNTRY
};
// ----------------------------------------------------------------------------
// wxLocaleForm: the forms of names understood by wxLocale::GetLocalizedName()
// ----------------------------------------------------------------------------
/**
The values understood by wxUILocale::GetLocalizedName().
The values specify the form of a localized name.
@since 3.1.6
@see wxUILocale::GetLocalizedName()
*/
enum wxLocaleForm
{
/// Name should be returned in the language of the locale itself.
wxLOCALE_FORM_NATIVE,
/// Name should be returned in English.
wxLOCALE_FORM_ENGLISH
};
/** /**
@class wxLocale @class wxLocale

View File

@@ -162,6 +162,11 @@ public:
*/ */
wxString GetName() const; wxString GetName() const;
/**
Get the locale id from which the current locale was instantiated.
*/
wxLocaleIdent GetLocaleId() const;
/** /**
Query the locale for the specified information. Query the locale for the specified information.
@@ -179,6 +184,26 @@ public:
wxString GetInfo(wxLocaleInfo index, wxString GetInfo(wxLocaleInfo index,
wxLocaleCategory cat = wxLOCALE_CAT_DEFAULT) const; wxLocaleCategory cat = wxLOCALE_CAT_DEFAULT) const;
/**
Query the locale for the specified localized name.
@param name
One of the elements of wxLocaleName enum.
@param form
The representation form requested.
@return
The localized name value or empty string if the function failed.
*/
wxString GetLocalizedName(wxLocaleName name, wxLocaleForm form) const;
/**
Query the layout direction of the current locale.
@return
The layout direction or wxLayout_Default if the function failed.
*/
wxLayoutDirection GetLayoutDirection() const;
/** /**
Return true if locale is supported on the current system. Return true if locale is supported on the current system.
@@ -210,6 +235,18 @@ public:
*/ */
static const wxLanguageInfo* FindLanguageInfo(const wxString& locale); static const wxLanguageInfo* FindLanguageInfo(const wxString& locale);
/**
This function may be used to find the language description structure for the
given locale, specified as a locale identifier.
Returns the information for the given language or @NULL if this language
is unknown. Note that even if the returned pointer is valid, the caller
should @e not delete it.
@see GetLanguageInfo()
*/
static const wxLanguageInfo* FindLanguageInfo(const wxLocaleIdent& localeId);
/** /**
Returns a pointer to wxLanguageInfo structure containing information about Returns a pointer to wxLanguageInfo structure containing information about
the given language or @NULL if this language is unknown. Note that even if the given language or @NULL if this language is unknown. Note that even if
@@ -274,7 +311,7 @@ wxString wxGetUIDateFormat();
There are two possible ways to construct wxLocaleIdent: There are two possible ways to construct wxLocaleIdent:
- You can either use fromTag() to create it from a string in the form - You can either use FromTag() to create it from a string in the form
@code language ["-" script] ["-" region] @endcode, corresponding to @code language ["-" script] ["-" region] @endcode, corresponding to
the subset of BCP 47 (https://www.rfc-editor.org/rfc/bcp/bcp47.txt) the subset of BCP 47 (https://www.rfc-editor.org/rfc/bcp/bcp47.txt)
syntax. syntax.
@@ -308,7 +345,39 @@ class wxLocaleIdent
{ {
public: public:
/** /**
Return the locale identifier corresponding to the given BCP 47-like tag. Return the locale identifier corresponding to the given locale tag.
This method accepts locale tags in various formats:
- BCP-47,
- Windows,
- POSIX, and
- macOS.
See section 2.01 of https://www.rfc-editor.org/rfc/bcp/bcp47.txt for the
full BCP-47 syntax. Here we fully support just the subset we're interested in:
- Normal language tags (not private use or grandfathered ones),
- Script, and
- Region.
Additionally platform-specific tags are supported:
- Extensions (without validity checks) (Windows only),
- Sortorder (Windows only)
- Charset (POSIX only), and
- Modifier (POSIX only).
Only language, script, and region are supported across all platforms.
The script tag is mapped to the modifier tag for POSIX platforms.
The script tag takes precedence, if a modifier is also specified.
The following tag syntax is accepted:
BCP-47: <language>[-<script>][-<region>][-<extension>]
Windows: <language>[-<script>][-<region>][-<extension>][_<sortorder>]
POSIX: <language>[_<region>][.<charset>][@<modifier>]
macOS: <language>[-<script>][_<region>]
The string must contain at least the language part (2 or 3 ASCII The string must contain at least the language part (2 or 3 ASCII
letters) and may contain script and region separated by dashes, i.e. letters) and may contain script and region separated by dashes, i.e.
@@ -363,7 +432,9 @@ public:
/** /**
Set script. Set script.
Note that script value is currently ignored under Unix systems. Note that under Unix systems the script value is currently mapped
to the modifier attribute using the script alias name, if the latter
is known. Otherwise it is ignored.
Return reference to `this` for method chaining. Return reference to `this` for method chaining.
@@ -391,15 +462,46 @@ public:
Note that this value is only used under Unix systems and simply ignored Note that this value is only used under Unix systems and simply ignored
under the other ones. under the other ones.
Note that under Unix systems the modifier value may represent a script
value. If the value corresponds to a valid script alias it is mapped
to the associated script tag.
Return reference to `this` for method chaining. Return reference to `this` for method chaining.
@param modifier @param modifier
Modifier is defined by ISO/IEC 15897. Modifier is a free-form text string.
It is a semi-colon separated list of identifiers, or name=value pairs.
*/ */
wxLocaleIdent& Modifier(const wxString& modifier); wxLocaleIdent& Modifier(const wxString& modifier);
/**
Set extension.
Note that this value is only used under Windows systems and simply ignored
under the other ones.
Return reference to @this for method chaining.
@param extension
Extension identifiers allow to support custom Windows locales.
They are usually not portable, not even from one Windows system
to the other.
*/
wxLocaleIdent& Extension(const wxString& extension);
/**
Set sortorder.
Note that this value is only used under Windows systems and simply ignored
under the other ones.
Return reference to @this for method chaining.
@param sortorder
Sortorder identifiers are defined in the Windows Development documentation:
https://docs.microsoft.com/en-us/windows/win32/intl/sort-order-identifiers.
*/
wxLocaleIdent& Sortorder(const wxString& sortorder);
/// Return the language part of the locale identifier. /// Return the language part of the locale identifier.
const wxString& GetLanguage() const; const wxString& GetLanguage() const;
@@ -415,15 +517,36 @@ public:
/// Return the modifier part of the locale identifier. /// Return the modifier part of the locale identifier.
const wxString& GetModifier() const; const wxString& GetModifier() const;
/// Return the extension part of the locale identifier.
const wxString& GetExtension() const;
/// Return the sortorder part of the locale identifier.
const wxString& GetSortorder() const;
/** /**
Construct platform dependent name. Construct platform dependent name.
Format: Format:
Windows: \<language\>-\<script\>-\<REGION\> Windows: \<language\>-\<script\>-\<REGION\>-\<extension\>_\<sortorder\>
Unix: \<language\>_\<REGION\>.\<charset\>@\<modifier\> Unix: \<language\>_\<REGION\>.\<charset\>@\{\<modifier\>\|\<scriptalias\>\}
MacOS: \<language\>-\<script\>_\<REGION\> MacOS: \<language\>-\<script\>_\<REGION\>
*/ */
wxString GetName() const; wxString GetName() const;
/**
Construct name in specified format.
Format:
Default: name as used in wxLocaleIdent::FromTag() or system format
System: name in platform-dependent format
Windows: \<language\>-\<script\>-\<REGION\>-\<extension\>_\<sortorder\>
Unix: \<language\>_\<REGION\>.\<charset\>@\<modifier\>
MacOS: \<language\>-\<script\>_\<REGION\>
BCP 47: \<language\>-\<script\>-\<REGION\>-\<extension\>
@param tagType
Value from wxLocaleTagType enum.
*/
wxString GetTag(wxLocaleTagType tagType = wxLOCALE_TAGTYPE_DEFAULT) const;
/** /**
Check if the locale is empty. Check if the locale is empty.

Binary file not shown.

View File

@@ -5,8 +5,8 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"POT-Creation-Date: 2021-11-11 23:05+0100\n" "POT-Creation-Date: 2021-12-10 10:06+0100\n"
"PO-Revision-Date: 2021-11-11 23:09+0100\n" "PO-Revision-Date: 2021-12-10 10:15+0100\n"
"Last-Translator: Ulrich Telle <ulrich@telle-online.eu>\n" "Last-Translator: Ulrich Telle <ulrich@telle-online.eu>\n"
"Language-Team: \n" "Language-Team: \n"
"Language: de\n" "Language: de\n"
@@ -103,37 +103,37 @@ msgstr "Testet die Übersetzung der Schaltflächen im Hinweisfenster"
#: internat.cpp:316 #: internat.cpp:316
msgid "item" msgid "item"
msgstr "Eintrag" msgstr "Eintrag ohne Kontext"
#: internat.cpp:317 #: internat.cpp:317
msgctxt "context_1" msgctxt "context_1"
msgid "item" msgid "item"
msgstr "Eintrag A" msgstr "Eintrag Kontext 1"
#: internat.cpp:318 #: internat.cpp:318
msgctxt "context_2" msgctxt "context_2"
msgid "item" msgid "item"
msgstr "Eintrag B" msgstr "Eintrag Kontext 2"
#: internat.cpp:319 internat.cpp:320 #: internat.cpp:319 internat.cpp:320
msgid "sing" msgid "sing"
msgid_plural "plur" msgid_plural "plur"
msgstr[0] "Einzahl" msgstr[0] "Einzahl ohne Kontext"
msgstr[1] "Mehrzahl" msgstr[1] "Mehrzahl ohne Kontext"
#: internat.cpp:321 internat.cpp:322 #: internat.cpp:321 internat.cpp:322
msgctxt "context_1" msgctxt "context_1"
msgid "sing" msgid "sing"
msgid_plural "plur" msgid_plural "plur"
msgstr[0] "Einzahl A" msgstr[0] "Einzahl Kontext 1"
msgstr[1] "Mehrzahl A" msgstr[1] "Mehrzahl Kontext 1"
#: internat.cpp:323 internat.cpp:324 #: internat.cpp:323 internat.cpp:324
msgctxt "context_2" msgctxt "context_2"
msgid "sing" msgid "sing"
msgid_plural "plur" msgid_plural "plur"
msgstr[0] "Einzahl B" msgstr[0] "Einzahl Kontext 2"
msgstr[1] "Mehrzahl B" msgstr[1] "Mehrzahl Kontext 2"
#: internat.cpp:328 #: internat.cpp:328
msgid "Show coreutils &help" msgid "Show coreutils &help"
@@ -161,24 +161,34 @@ msgstr "&Hilfe"
msgid "Current UI locale: %s; C locale: %s" msgid "Current UI locale: %s; C locale: %s"
msgstr "Aktuelles UI Gebietsschema: %s; C Gebietsschema: %s" msgstr "Aktuelles UI Gebietsschema: %s; C Gebietsschema: %s"
#: internat.cpp:372 #: internat.cpp:373
#, c-format
msgid "UI locale name in English: %s"
msgstr "Name des UI Gebietsschema auf Englisch: %s"
#: internat.cpp:385
#, c-format
msgid "... and in the locale language: %s"
msgstr "… und in der Sprache des Gebietsschemas: %s"
#: internat.cpp:396
msgid "Numeric input:" msgid "Numeric input:"
msgstr "Numerische Eingabe:" msgstr "Numerische Eingabe:"
#: internat.cpp:393 #: internat.cpp:417
msgid "Number" msgid "Number"
msgstr "Zahl" msgstr "Zahl"
#: internat.cpp:397 #: internat.cpp:421
msgid "Date" msgid "Date"
msgstr "Datum" msgstr "Datum"
#: internat.cpp:413 #: internat.cpp:437
#, c-format #, c-format
msgid "Number in UI locale: %s; in C locale: %.2f" msgid "Number in UI locale: %s; in C locale: %.2f"
msgstr "Zahl im UI Gebietsschema: %s; im C Gebietsschema: %.2f" msgstr "Zahl im UI Gebietsschema: %s; im C Gebietsschema: %.2f"
#: internat.cpp:433 #: internat.cpp:457
msgid "" msgid ""
"I18n sample\n" "I18n sample\n"
"(c) 1998, 1999 Vadim Zeitlin and Julian Smart" "(c) 1998, 1999 Vadim Zeitlin and Julian Smart"
@@ -186,108 +196,111 @@ msgstr ""
"I18n Beispiel\n" "I18n Beispiel\n"
"© 1998, 1999 Vadim Zeitlin und Julian Smart" "© 1998, 1999 Vadim Zeitlin und Julian Smart"
#: internat.cpp:434 #: internat.cpp:458
msgid "About Internat" msgid "About Internat"
msgstr "Über Internat" msgstr "Über Internat"
#: internat.cpp:467 #: internat.cpp:491
msgid "Enter your number:" msgid "Enter your number:"
msgstr "Geben Sie eine Zahl ein:" msgstr "Geben Sie eine Zahl ein:"
#: internat.cpp:468 #: internat.cpp:492
msgid "Try to guess my number!" msgid "Try to guess my number!"
msgstr "Versuchen Sie, die Zahl zu raten!" msgstr "Versuchen Sie, die Zahl zu raten!"
#: internat.cpp:482 #: internat.cpp:506
msgid "You've probably entered an invalid number." msgid "You've probably entered an invalid number."
msgstr "Sie haben wahrscheinlich eine ungültige Zahl eingegeben." msgstr "Sie haben wahrscheinlich eine ungültige Zahl eingegeben."
#: internat.cpp:496 #: internat.cpp:520
msgid "Congratulations! you've won. Here is the magic phrase:" msgid "Congratulations! you've won. Here is the magic phrase:"
msgstr "" msgstr ""
"Herzlichen Glückwunsch! Sie haben gewonnen. Hier ist der magische Satz:" "Herzlichen Glückwunsch! Sie haben gewonnen. Hier ist der magische Satz:"
#: internat.cpp:497 #: internat.cpp:521
#, c-format #, c-format
msgid "cannot create fifo `%s'" msgid "cannot create fifo `%s'"
msgstr "Fifo `%s' kann nicht erzeugt werden" msgstr "Fifo `%s' kann nicht erzeugt werden"
#: internat.cpp:506 #: internat.cpp:530
msgid "Bad luck! try again..." msgid "Bad luck! try again..."
msgstr "Pech gehabt! Versuchen Sie es noch einmal..." msgstr "Pech gehabt! Versuchen Sie es noch einmal..."
#: internat.cpp:514 #: internat.cpp:538
msgid "Result" msgid "Result"
msgstr "Ergebnis" msgstr "Ergebnis"
#: internat.cpp:522 #: internat.cpp:546
msgid "Enter the locale to test" msgid "Enter the locale to test"
msgstr "Geben Sie das zu testende Gebietsschema ein" msgstr "Geben Sie das zu testende Gebietsschema ein"
#: internat.cpp:534 #: internat.cpp:562
#, c-format #, c-format
msgid "Locale \"%s\" is unknown." msgid ""
msgstr "Gebietsschema \"%s\" ist unbekannt." "Locale \"%s\" is available.\n"
"Identifier: %s; Layout: %s\n"
"English name: %s\n"
"Localized name: %s"
msgstr ""
"Gebietsschema \"%s\" ist verfügbar.\n"
"Identifikator: %s; Layout: %s\n"
"Englischer Name: %s\n"
"Lokaler Name: %s"
#: internat.cpp:540 #: internat.cpp:569
#, c-format
msgid "Locale \"%s\" is available."
msgstr "Gebietsschema \"%s\" ist verfügbar."
#: internat.cpp:544
#, c-format #, c-format
msgid "Locale \"%s\" is not available." msgid "Locale \"%s\" is not available."
msgstr "Gebietsschema \"%s\" ist nicht verfügbar." msgstr "Gebietsschema \"%s\" ist nicht verfügbar."
#: internat.cpp:559 #: internat.cpp:584
msgid "Dummy file dialog" msgid "Dummy file dialog"
msgstr "Dummy-Datei-Dialog" msgstr "Dummy-Datei-Dialog"
#: internat.cpp:564 #: internat.cpp:589
msgid "Testing _() (gettext)" msgid "Testing _() (gettext)"
msgstr "Test von _() (gettext)" msgstr "Test von _() (gettext)"
#: internat.cpp:572 #: internat.cpp:597
msgid "Please enter text to translate" msgid "Please enter text to translate"
msgstr "Bitte geben Sie einen zu übersetzenden Text ein" msgstr "Bitte geben Sie einen zu übersetzenden Text ein"
#: internat.cpp:573 #: internat.cpp:598
msgid "default value" msgid "default value"
msgstr "Default-Wert" msgstr "Default-Wert"
#: internat.cpp:587 #: internat.cpp:612
msgid "Testing _N() (ngettext)" msgid "Testing _N() (ngettext)"
msgstr "Test von _N() (ngettext)" msgstr "Test von _N() (ngettext)"
#: internat.cpp:589 #: internat.cpp:614
msgid "Please enter range for plural forms of \"n files deleted\" phrase" msgid "Please enter range for plural forms of \"n files deleted\" phrase"
msgstr "" msgstr ""
"Bitte geben Sie einen Bereich für Pluralformen des Satzes \"n Dateien " "Bitte geben Sie einen Bereich für Pluralformen des Satzes \"n Dateien "
"gelöscht\" ein" "gelöscht\" ein"
#: internat.cpp:601 #: internat.cpp:626
msgid "file deleted" msgid "file deleted"
msgid_plural "files deleted" msgid_plural "files deleted"
msgstr[0] "Datei gelöscht" msgstr[0] "Datei gelöscht"
msgstr[1] "Dateien gelöscht" msgstr[1] "Dateien gelöscht"
#: internat.cpp:612 #: internat.cpp:637
msgid "line 1" msgid "line 1"
msgstr "Zeile 1" msgstr "Zeile 1"
#: internat.cpp:613 #: internat.cpp:638
msgid "line 2" msgid "line 2"
msgstr "Zeile 2" msgstr "Zeile 2"
#: internat.cpp:614 #: internat.cpp:639
msgid "line 3" msgid "line 3"
msgstr "Zeile 3" msgstr "Zeile 3"
#: internat.cpp:617 #: internat.cpp:642
msgid "Testing wxTRANSLATE() (gettext_noop)" msgid "Testing wxTRANSLATE() (gettext_noop)"
msgstr "Test von wxTRANSLATE() (gettext_noop)" msgstr "Test von wxTRANSLATE() (gettext_noop)"
#: internat.cpp:630 #: internat.cpp:655
msgid "" msgid ""
"Are the labels of the buttons in this message box translated into the " "Are the labels of the buttons in this message box translated into the "
"current locale language?" "current locale language?"
@@ -295,10 +308,18 @@ msgstr ""
"Wurden die Beschriftungen der Schaltflächen in diesem Hinweisfenster in die " "Wurden die Beschriftungen der Schaltflächen in diesem Hinweisfenster in die "
"Sprache des aktuellen Gebietsschemas übersetzt?" "Sprache des aktuellen Gebietsschemas übersetzt?"
#: internat.cpp:632 #: internat.cpp:657
msgid "wxWidgets i18n sample" msgid "wxWidgets i18n sample"
msgstr "wxWidgets I18n Beispiel" msgstr "wxWidgets I18n Beispiel"
#: internat.cpp:637 #: internat.cpp:662
msgid "Please report the details of your platform to us." msgid "Please report the details of your platform to us."
msgstr "Bitte teilen Sie uns über die Details Ihrer Plattform mit." msgstr "Bitte teilen Sie uns über die Details Ihrer Plattform mit."
#, c-format
#~ msgid "Locale \"%s\" is unknown."
#~ msgstr "Gebietsschema \"%s\" ist unbekannt."
#, c-format
#~ msgid "Locale \"%s\" is available."
#~ msgstr "Gebietsschema \"%s\" ist verfügbar."

Binary file not shown.

View File

@@ -5,101 +5,310 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: wxWindows 2.0 i18n sample\n" "Project-Id-Version: wxWindows 2.0 i18n sample\n"
"POT-Creation-Date: 1999-01-13 18:19+0100\n" "POT-Creation-Date: 2021-12-10 10:11+0100\n"
"PO-Revision-Date: 2016-09-20 19:38+0200\n" "PO-Revision-Date: 2021-12-10 10:12+0100\n"
"Last-Translator: Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>\n" "Last-Translator: Ulrich Telle <ulrich@telle-online.eu>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"Language-Team: \n" "Language-Team: \n"
"Language: fr\n" "Language: fr\n"
"X-Generator: Poedit 1.8.7\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Generator: Poedit 3.0\n"
"X-Poedit-KeywordsList: _;wxTRANSLATE;wxPLURAL:1,2;wxGETTEXT_IN_CONTEXT:1c,2;"
"wxGETTEXT_IN_CONTEXT_PLURAL:1c,2,3\n"
"X-Poedit-Basepath: ..\n"
"X-Poedit-SearchPath-0: internat.cpp\n"
#: internat.cpp:168
msgid "skip setting locale on startup"
msgstr ""
#: internat.cpp:170
msgid "do set locale on startup without asking"
msgstr ""
#: internat.cpp:253
#, c-format
msgid "Couldn't find/load 'internat' catalog for %s."
msgstr ""
#: internat.cpp:289
#, fuzzy
#| msgid "International wxWindows App"
msgid "International wxWidgets App"
msgstr "Application wxWindows internationale"
#: internat.cpp:295
msgid "&Test locale availability...\tCtrl-T"
msgstr ""
#: internat.cpp:300
msgid "E&xit"
msgstr "&Quitter"
#: internat.cpp:303
msgid "&Open bogus file"
msgstr "&Ouvrir un fichier"
#: internat.cpp:303
msgid "Shows a wxWidgets localized error message"
msgstr ""
#: internat.cpp:304
msgid "&Save dummy file"
msgstr ""
#: internat.cpp:304
msgid "Shows a localized standard dialog"
msgstr ""
#: internat.cpp:305
msgid "&Play a game"
msgstr "&Jouer"
#: internat.cpp:305
msgid "A little game; hint: 17 is a lucky number for many"
msgstr ""
#: internat.cpp:307
msgid "&1 _() (gettext)"
msgstr ""
#: internat.cpp:307
msgid "Tests the _() macro"
msgstr ""
#: internat.cpp:308
msgid "&2 _N() (ngettext)"
msgstr ""
#: internat.cpp:308
msgid "Tests the _N() macro"
msgstr ""
#: internat.cpp:309
msgid "&3 wxTRANSLATE() (gettext_noop)"
msgstr ""
#: internat.cpp:309
msgid "Tests the wxTRANSLATE() macro"
msgstr ""
#: internat.cpp:310
msgid "&Message box test"
msgstr ""
#: internat.cpp:311
msgid "Tests message box buttons labels translation"
msgstr ""
#: internat.cpp:316
msgid "item" msgid "item"
msgstr "Item without context" msgstr "Item without context"
#: internat.cpp:317
msgctxt "context_1" msgctxt "context_1"
msgid "item" msgid "item"
msgstr "Item in context 1" msgstr "Item in context 1"
#: internat.cpp:318
msgctxt "context_2" msgctxt "context_2"
msgid "item" msgid "item"
msgstr "Item in context 2" msgstr "Item in context 2"
#: internat.cpp:319 internat.cpp:320
msgid "sing" msgid "sing"
msgid_plural "plur" msgid_plural "plur"
msgstr[0] "Singular form without context" msgstr[0] "Singular form without context"
msgstr[1] "Plural form without context" msgstr[1] "Plural form without context"
#: internat.cpp:321 internat.cpp:322
msgctxt "context_1" msgctxt "context_1"
msgid "sing" msgid "sing"
msgid_plural "plur" msgid_plural "plur"
msgstr[0] "Singular form in context 1" msgstr[0] "Singular form in context 1"
msgstr[1] "Plural form in context 1" msgstr[1] "Plural form in context 1"
#: internat.cpp:323 internat.cpp:324
msgctxt "context_2" msgctxt "context_2"
msgid "sing" msgid "sing"
msgid_plural "plur" msgid_plural "plur"
msgstr[0] "Singular form in context 2" msgstr[0] "Singular form in context 2"
msgstr[1] "Plural form in context 2" msgstr[1] "Plural form in context 2"
#: internat.cpp:98 #: internat.cpp:328
msgid "International wxWindows App" msgid "Show coreutils &help"
msgstr "Application wxWindows internationale" msgstr ""
#: internat.cpp:105 #: internat.cpp:331
msgid "&About" msgid "&About"
msgstr "&A propos" msgstr "&A propos"
#: internat.cpp:107 #: internat.cpp:336
msgid "E&xit"
msgstr "&Quitter"
#: internat.cpp:110
msgid "&Open bogus file"
msgstr "&Ouvrir un fichier"
#: internat.cpp:111
msgid "&Play a game"
msgstr "&Jouer"
#: internat.cpp:115
msgid "&Test" msgid "&Test"
msgstr "&Test" msgstr "&Test"
#: internat.cpp:138 #: internat.cpp:337
msgid "&Macro"
msgstr ""
#: internat.cpp:343
msgctxt "standard Windows menu"
msgid "&Help"
msgstr ""
#: internat.cpp:360
#, c-format
msgid "Current UI locale: %s; C locale: %s"
msgstr ""
#: internat.cpp:373
#, c-format
msgid "UI locale name in English: %s"
msgstr ""
#: internat.cpp:385
#, c-format
msgid "... and in the locale language: %s"
msgstr ""
#: internat.cpp:396
msgid "Numeric input:"
msgstr ""
#: internat.cpp:417
msgid "Number"
msgstr ""
#: internat.cpp:421
msgid "Date"
msgstr ""
#: internat.cpp:437
#, c-format
msgid "Number in UI locale: %s; in C locale: %.2f"
msgstr ""
#: internat.cpp:457
#, fuzzy
#| msgid ""
#| "I18n sample\n"
#| "© 1998, 1999 Vadim Zeitlin and Julian Smart"
msgid "" msgid ""
"I18n sample\n" "I18n sample\n"
"© 1998, 1999 Vadim Zeitlin and Julian Smart" "(c) 1998, 1999 Vadim Zeitlin and Julian Smart"
msgstr "" msgstr ""
"Exemple d'i18n\n" "Exemple d'i18n\n"
"© 1998, 1999 Vadim Zeitlin et Julian Smart" "© 1998, 1999 Vadim Zeitlin et Julian Smart"
#: internat.cpp:139 #: internat.cpp:458
msgid "About Internat" msgid "About Internat"
msgstr "A propos d'Internat" msgstr "A propos d'Internat"
#: internat.cpp:144 #: internat.cpp:491
msgid "Enter your number:" msgid "Enter your number:"
msgstr "Entrez votre numéro:" msgstr "Entrez votre numéro:"
#: internat.cpp:145 #: internat.cpp:492
msgid "Try to guess my number!" msgid "Try to guess my number!"
msgstr "Essayez de déviner mon numéro!" msgstr "Essayez de déviner mon numéro!"
#: internat.cpp:150 #: internat.cpp:506
msgid "You've probably entered an invalid number." msgid "You've probably entered an invalid number."
msgstr "Vous avez probablement entré un nombre invalide." msgstr "Vous avez probablement entré un nombre invalide."
#: internat.cpp:154 #: internat.cpp:520
msgid "Bad luck! try again..."
msgstr "Pas de chance! essayez encore..."
#: internat.cpp:158
msgid "Congratulations! you've won. Here is the magic phrase:" msgid "Congratulations! you've won. Here is the magic phrase:"
msgstr "Félicitations! vouz avez gagné. Voilà la phrase magique:" msgstr "Félicitations! vouz avez gagné. Voilà la phrase magique:"
#: internat.cpp:162 #: internat.cpp:521
#, c-format
msgid "cannot create fifo `%s'"
msgstr ""
#: internat.cpp:530
msgid "Bad luck! try again..."
msgstr "Pas de chance! essayez encore..."
#: internat.cpp:538
msgid "Result" msgid "Result"
msgstr "Resultat" msgstr "Resultat"
#: internat.cpp:546
msgid "Enter the locale to test"
msgstr ""
#: internat.cpp:562
#, c-format
msgid ""
"Locale \"%s\" is available.\n"
"Identifier: %s; Layout: %s\n"
"English name: %s\n"
"Localized name: %s"
msgstr ""
#: internat.cpp:569
#, c-format
msgid "Locale \"%s\" is not available."
msgstr ""
#: internat.cpp:584
msgid "Dummy file dialog"
msgstr ""
#: internat.cpp:589
msgid "Testing _() (gettext)"
msgstr ""
#: internat.cpp:597
msgid "Please enter text to translate"
msgstr ""
#: internat.cpp:598
msgid "default value"
msgstr ""
#: internat.cpp:612
msgid "Testing _N() (ngettext)"
msgstr ""
#: internat.cpp:614
msgid "Please enter range for plural forms of \"n files deleted\" phrase"
msgstr ""
#: internat.cpp:626
msgid "file deleted"
msgid_plural "files deleted"
msgstr[0] ""
msgstr[1] ""
#: internat.cpp:637
msgid "line 1"
msgstr ""
#: internat.cpp:638
msgid "line 2"
msgstr ""
#: internat.cpp:639
msgid "line 3"
msgstr ""
#: internat.cpp:642
msgid "Testing wxTRANSLATE() (gettext_noop)"
msgstr ""
#: internat.cpp:655
msgid ""
"Are the labels of the buttons in this message box translated into the "
"current locale language?"
msgstr ""
#: internat.cpp:657
msgid "wxWidgets i18n sample"
msgstr ""
#: internat.cpp:662
msgid "Please report the details of your platform to us."
msgstr ""

View File

@@ -364,6 +364,30 @@ MyFrame::MyFrame()
), ),
wxSizerFlags().Center().Border()); wxSizerFlags().Center().Border());
topSizer->Add(new wxStaticText
(
panel,
wxID_ANY,
wxString::Format
(
_("UI locale name in English: %s"),
wxUILocale::GetCurrent().GetLocalizedName(wxLOCALE_NAME_LOCALE, wxLOCALE_FORM_ENGLISH)
)
),
wxSizerFlags().Center().Border());
topSizer->Add(new wxStaticText
(
panel,
wxID_ANY,
wxString::Format
(
_("... and in the locale language: %s"),
wxUILocale::GetCurrent().GetLocalizedName(wxLOCALE_NAME_LOCALE, wxLOCALE_FORM_NATIVE)
)
),
wxSizerFlags().Center().Border());
// create some controls affected by the locale // create some controls affected by the locale
// this demonstrates RTL layout mirroring for Arabic locales and using // this demonstrates RTL layout mirroring for Arabic locales and using
@@ -528,16 +552,17 @@ void MyFrame::OnTestLocaleAvail(wxCommandEvent& WXUNUSED(event))
return; return;
s_locale = locale; s_locale = locale;
const wxLanguageInfo * const info = wxLocale::FindLanguageInfo(s_locale);
if ( !info )
{
wxLogError(_("Locale \"%s\" is unknown."), s_locale);
return;
}
if ( wxLocale::IsAvailable(info->Language) ) wxUILocale uiLocale = wxUILocale::FromTag(s_locale);
if (uiLocale.IsSupported())
{ {
wxLogMessage(_("Locale \"%s\" is available."), s_locale); wxLayoutDirection layout = uiLocale.GetLayoutDirection();
wxString strLayout = (layout == wxLayout_RightToLeft) ? "RTL" : "LTR";
wxString strLocale = uiLocale.GetLocalizedName(wxLOCALE_NAME_LOCALE, wxLOCALE_FORM_NATIVE);
wxLogMessage(_("Locale \"%s\" is available.\nIdentifier: %s; Layout: %s\nEnglish name: %s\nLocalized name: %s"),
s_locale, uiLocale.GetName(), strLayout,
uiLocale.GetLocalizedName(wxLOCALE_NAME_LOCALE, wxLOCALE_FORM_ENGLISH),
uiLocale.GetLocalizedName(wxLOCALE_NAME_LOCALE, wxLOCALE_FORM_NATIVE));
} }
else else
{ {

View File

@@ -166,24 +166,14 @@ const char* wxLanguageInfo::TrySetLocale() const
wxString wxLanguageInfo::GetLocaleName() const wxString wxLanguageInfo::GetLocaleName() const
{ {
const char* const orig = wxSetlocale(LC_ALL, NULL); wxString localeId = CanonicalRef.empty() ? CanonicalName : CanonicalRef;
wxUILocale uiLocale = wxUILocale::FromTag(localeId);
const char* const ret = TrySetLocale(); wxString localeName = uiLocale.IsSupported() ? uiLocale.GetName() : wxString();
wxString retval; return localeName;
if ( ret )
{
// Note that we must copy the returned value before calling setlocale()
// again as the string "ret" points to can (and, at least under Linux
// with glibc, actually always will) be changed by this call.
retval = ret;
wxSetlocale(LC_ALL, orig);
}
return retval;
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// wxUILocale / wxLocale // wxUILocale
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
static wxLanguageInfos gs_languagesDB; static wxLanguageInfos gs_languagesDB;
@@ -217,233 +207,6 @@ void wxUILocale::DestroyLanguagesDB()
} }
} }
namespace
{
#if defined(__UNIX__) && !defined(__WXOSX__)
// Small helper function: get the value of the given environment variable and
// return true only if the variable was found and has non-empty value.
inline bool wxGetNonEmptyEnvVar(const wxString& name, wxString* value)
{
return wxGetEnv(name, value) && !value->empty();
}
#endif
} // anonymous namespace
/*static*/
int wxUILocale::GetSystemLanguage()
{
CreateLanguagesDB();
// init i to avoid compiler warning
size_t i = 0,
count = gs_languagesDB.size();
#ifdef __WXOSX__
wxCFRef<CFLocaleRef> userLocaleRef(CFLocaleCopyCurrent());
// because the locale identifier (kCFLocaleIdentifier) is formatted a little bit differently, eg
// az_Cyrl_AZ@calendar=buddhist;currency=JPY we just recreate the base info as expected by wx here
wxCFStringRef str(wxCFRetain((CFStringRef)CFLocaleGetValue(userLocaleRef, kCFLocaleLanguageCode)));
const wxString langPrefix = str.AsString() + "_";
str.reset(wxCFRetain((CFStringRef)CFLocaleGetValue(userLocaleRef, kCFLocaleCountryCode)));
const wxString langFull = langPrefix + str.AsString();
int langOnlyMatchIndex = wxNOT_FOUND;
for (i = 0; i < count; i++)
{
const wxString& fullname = gs_languagesDB[i].CanonicalName;
if (langFull == fullname)
{
// Exact match, no need to look any further.
break;
}
if (fullname.StartsWith(langPrefix))
{
// Matched just the language, keep looking, but we'll keep this if
// we don't find an exact match later.
langOnlyMatchIndex = i;
}
}
if (i == count && langOnlyMatchIndex != wxNOT_FOUND)
i = langOnlyMatchIndex;
#elif defined(__UNIX__)
// first get the string identifying the language from the environment
wxString langFull;
if (!wxGetNonEmptyEnvVar(wxS("LC_ALL"), &langFull) &&
!wxGetNonEmptyEnvVar(wxS("LC_MESSAGES"), &langFull) &&
!wxGetNonEmptyEnvVar(wxS("LANG"), &langFull))
{
// no language specified, treat it as English
return wxLANGUAGE_ENGLISH_US;
}
// the language string has the following form
//
// lang[_LANG][.encoding][@modifier]
//
// (see environ(5) in the Open Unix specification)
//
// where lang is the primary language, LANG is a sublang/territory,
// encoding is the charset to use and modifier "allows the user to select
// a specific instance of localization data within a single category"
//
// for example, the following strings are valid:
// fr
// fr_FR
// de_DE.iso88591
// de_DE@euro
// de_DE.iso88591@euro
// for now we don't use the encoding, although we probably should (doing
// translations of the msg catalogs on the fly as required) (TODO)
//
// we need the modified for languages like Valencian: ca_ES@valencia
// though, remember it
wxString modifier;
size_t posModifier = langFull.find_first_of(wxS("@"));
if (posModifier != wxString::npos)
modifier = langFull.Mid(posModifier);
size_t posEndLang = langFull.find_first_of(wxS("@."));
if (posEndLang != wxString::npos)
{
langFull.Truncate(posEndLang);
}
if (langFull == wxS("C") || langFull == wxS("POSIX"))
{
// default C locale is English too
return wxLANGUAGE_ENGLISH_US;
}
// do we have just the language (or sublang too)?
const bool justLang = langFull.find('_') == wxString::npos;
// 0. Make sure the lang is according to latest ISO 639
// (this is necessary because glibc uses iw and in instead
// of he and id respectively).
// the language itself (second part is the dialect/sublang)
wxString langOrig = ExtractLang(langFull);
wxString lang;
if (langOrig == wxS("iw"))
lang = wxS("he");
else if (langOrig == wxS("in"))
lang = wxS("id");
else if (langOrig == wxS("ji"))
lang = wxS("yi");
else if (langOrig == wxS("no_NO"))
lang = wxS("nb_NO");
else if (langOrig == wxS("no_NY"))
lang = wxS("nn_NO");
else if (langOrig == wxS("no"))
lang = wxS("nb_NO");
else
lang = langOrig;
// did we change it?
if (lang != langOrig)
{
langFull = lang + ExtractNotLang(langFull);
}
// 1. Try to find the language either as is:
// a) With modifier if set
if (!modifier.empty())
{
wxString langFullWithModifier = langFull + modifier;
for (i = 0; i < count; i++)
{
if (gs_languagesDB[i].CanonicalName == langFullWithModifier)
break;
}
}
// b) Without modifier
if (modifier.empty() || i == count)
{
for (i = 0; i < count; i++)
{
if (gs_languagesDB[i].CanonicalName == langFull)
break;
}
}
// 2. If langFull is of the form xx_YY, try to find xx:
if (i == count && !justLang)
{
for (i = 0; i < count; i++)
{
if (ExtractLang(gs_languagesDB[i].CanonicalName) == lang)
{
break;
}
}
}
// 3. If langFull is of the form xx, try to find any xx_YY record:
if (i == count && justLang)
{
for (i = 0; i < count; i++)
{
if (ExtractLang(gs_languagesDB[i].CanonicalName) == langFull)
{
break;
}
}
}
if (i == count)
{
// In addition to the format above, we also can have full language
// names in LANG env var - for example, SuSE is known to use
// LANG="german" - so check for use of non-standard format and try to
// find the name in verbose description.
for (i = 0; i < count; i++)
{
if (gs_languagesDB[i].Description.CmpNoCase(langFull) == 0)
{
break;
}
}
}
#elif defined(__WIN32__)
const LANGID langid = ::GetUserDefaultUILanguage();
if (langid != LOCALE_CUSTOM_UI_DEFAULT)
{
wxUint32 lang = PRIMARYLANGID(langid);
wxUint32 sublang = SUBLANGID(langid);
for (i = 0; i < count; i++)
{
if (gs_languagesDB[i].WinLang == lang &&
gs_languagesDB[i].WinSublang == sublang)
{
break;
}
}
}
//else: leave wxlang == wxLANGUAGE_UNKNOWN
#endif // Unix/Win32
if (i < count)
{
// we did find a matching entry, use it
return gs_languagesDB[i].Language;
}
// no info about this language in the database
return wxLANGUAGE_UNKNOWN;
}
/* static */ /* static */
void wxUILocale::AddLanguage(const wxLanguageInfo& info) void wxUILocale::AddLanguage(const wxLanguageInfo& info)
{ {
@@ -451,95 +214,6 @@ void wxUILocale::AddLanguage(const wxLanguageInfo& info)
gs_languagesDB.push_back(info); gs_languagesDB.push_back(info);
} }
/* static */
const wxLanguageInfo* wxUILocale::GetLanguageInfo(int lang)
{
CreateLanguagesDB();
// calling GetLanguageInfo(wxLANGUAGE_DEFAULT) is a natural thing to do, so
// make it work
if (lang == wxLANGUAGE_DEFAULT)
lang = GetSystemLanguage();
if (lang == wxLANGUAGE_UNKNOWN)
return NULL;
const size_t count = gs_languagesDB.size();
for (size_t i = 0; i < count; i++)
{
if (gs_languagesDB[i].Language == lang)
return &gs_languagesDB[i];
}
return NULL;
}
/* static */
wxString wxUILocale::GetLanguageName(int lang)
{
wxString string;
if (lang == wxLANGUAGE_DEFAULT || lang == wxLANGUAGE_UNKNOWN)
return string;
const wxLanguageInfo* info = GetLanguageInfo(lang);
if (info)
string = info->Description;
return string;
}
/* static */
wxString wxUILocale::GetLanguageCanonicalName(int lang)
{
wxString string;
if (lang == wxLANGUAGE_DEFAULT || lang == wxLANGUAGE_UNKNOWN)
return string;
const wxLanguageInfo* info = GetLanguageInfo(lang);
if (info)
string = info->CanonicalName;
return string;
}
/* static */
const wxLanguageInfo* wxUILocale::FindLanguageInfo(const wxString& locale)
{
CreateLanguagesDB();
const wxLanguageInfo* infoRet = NULL;
const size_t count = gs_languagesDB.size();
for (size_t i = 0; i < count; i++)
{
const wxLanguageInfo* info = &gs_languagesDB[i];
if (wxStricmp(locale, info->CanonicalName) == 0 ||
wxStricmp(locale, info->Description) == 0)
{
// exact match, stop searching
infoRet = info;
break;
}
if (wxStricmp(locale, info->CanonicalName.BeforeFirst(wxS('_'))) == 0)
{
// a match -- but maybe we'll find an exact one later, so continue
// looking
//
// OTOH, maybe we had already found a language match and in this
// case don't overwrite it because the entry for the default
// country always appears first in gs_languagesDB
if (!infoRet)
infoRet = info;
}
}
return infoRet;
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// wxLocale // wxLocale
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@@ -629,9 +303,25 @@ bool wxLocale::Init(const wxString& name,
DoInit(name, strShort, wxLANGUAGE_UNKNOWN); DoInit(name, strShort, wxLANGUAGE_UNKNOWN);
const bool ret = wxSetlocale(LC_ALL, szLocale) != NULL; #if defined(__UNIX__) || defined(__WIN32__)
const wxString oldUILocale = wxUILocale::GetCurrent().GetName();
bool ok = wxUILocale::UseLocaleName(szLocale);
if (ok)
{
m_oldUILocale = oldUILocale;
}
return DoCommonPostInit(ret, szLocale, shortName, bLoadDefault); // Under (non-Darwn) Unix wxUILocale already set the C locale, but under
// the other platforms we still have to do it here.
#if defined(__WIN32__) || defined(__WXOSX__)
ok = wxSetlocale(LC_ALL, szLocale) != NULL;
#endif // __WIN32__
#else
bool ok = false;
#endif
return DoCommonPostInit(ok, szLocale, shortName, bLoadDefault);
} }
void wxLocale::DoInit(const wxString& name, void wxLocale::DoInit(const wxString& name,
@@ -740,7 +430,7 @@ bool wxLocale::Init(int lang, int flags)
else else
{ {
name = info->Description; name = info->Description;
shortName = info->CanonicalName; shortName = info->CanonicalRef.empty() ? info->CanonicalName : info->CanonicalRef;
} }
DoInit(name, shortName, lang); DoInit(name, shortName, lang);
@@ -748,9 +438,14 @@ bool wxLocale::Init(int lang, int flags)
// Set the locale: // Set the locale:
#if defined(__UNIX__) || defined(__WIN32__) #if defined(__UNIX__) || defined(__WIN32__)
const wxString oldUILocale = wxUILocale::GetCurrent().GetName();
bool ok = lang == wxLANGUAGE_DEFAULT ? wxUILocale::UseDefault() bool ok = lang == wxLANGUAGE_DEFAULT ? wxUILocale::UseDefault()
: wxUILocale::UseLanguage(*info); : wxUILocale::UseLocaleName(shortName);
if (ok)
{
m_oldUILocale = oldUILocale;
}
// Under (non-Darwn) Unix wxUILocale already set the C locale, but under // Under (non-Darwn) Unix wxUILocale already set the C locale, but under
// the other platforms we still have to do it here. // the other platforms we still have to do it here.
@@ -1015,15 +710,17 @@ wxLocale::~wxLocale()
// restore old locale pointer // restore old locale pointer
wxSetLocale(m_pOldLocale); wxSetLocale(m_pOldLocale);
// and old current wxUILocale
if (!m_oldUILocale.empty())
{
wxUILocale::UseLocaleName(m_oldUILocale);
}
if ( m_pszOldLocale ) if ( m_pszOldLocale )
{ {
wxSetlocale(LC_ALL, m_pszOldLocale); wxSetlocale(LC_ALL, m_pszOldLocale);
free(const_cast<char *>(m_pszOldLocale)); free(const_cast<char *>(m_pszOldLocale));
} }
#ifdef __WIN32__
wxUseLCID(m_oldLCID);
#endif
} }
@@ -1041,51 +738,10 @@ bool wxLocale::IsAvailable(int lang)
return false; return false;
} }
#if defined(__WIN32__) wxString localeTag = info->CanonicalRef.empty() ? info->LocaleTag : info->CanonicalRef;
if ( !info->WinLang ) wxUILocale uiLocale(wxLocaleIdent::FromTag(localeTag));
return false;
if ( !::IsValidLocale(info->GetLCID(), LCID_INSTALLED) ) return uiLocale.IsSupported();
return false;
#elif defined(__WXOSX__)
CFLocaleRef
cfloc = CFLocaleCreate(kCFAllocatorDefault, wxCFStringRef(info->CanonicalName));
if ( !cfloc )
return false;
CFRelease(cfloc);
#elif defined(__UNIX__)
// Test if setting the locale works, then set it back.
char * const oldLocale = wxStrdupA(setlocale(LC_ALL, NULL));
wxLocaleIdent locId;
wxString region;
locId.Language(info->CanonicalName.BeforeFirst('_', &region));
if ( !region.empty() )
{
// We never have encoding in our canonical names, but we can have
// modifiers, so take them into account.
wxString mod;
locId.Region(region.BeforeFirst('@', &mod));
if ( !mod.empty() )
locId.Modifier(mod);
}
const bool available = wxSetlocaleTryAll(LC_ALL, locId);
// restore the original locale
wxSetlocale(LC_ALL, oldLocale);
free(oldLocale);
if ( !available )
return false;
#endif
return true;
} }
@@ -1285,50 +941,50 @@ wxString wxTranslateFromUnicodeFormat(const wxString& fmt)
break; break;
case 'c': case 'c':
switch ( lastCount ) switch ( lastCount )
{ {
case 1: // c case 1: // c
// TODO: unsupported: first day of week as numeric value // TODO: unsupported: first day of week as numeric value
fmtWX += "1"; fmtWX += "1";
break; break;
case 3: // ccc case 3: // ccc
fmtWX += "%a"; fmtWX += "%a";
break; break;
case 4: // cccc case 4: // cccc
fmtWX += "%A"; fmtWX += "%A";
break; break;
case 5: // ccccc case 5: // ccccc
// no "narrow form" in strftime(), use abbrev. // no "narrow form" in strftime(), use abbrev.
fmtWX += "%a"; fmtWX += "%a";
break; break;
default: default:
wxFAIL_MSG( "wrong number of 'c's" ); wxFAIL_MSG( "wrong number of 'c's" );
} }
break; break;
case 'L': case 'L':
switch ( lastCount ) switch ( lastCount )
{ {
case 1: // L case 1: // L
case 2: // LL case 2: // LL
fmtWX += "%m"; fmtWX += "%m";
break; break;
case 3: // LLL case 3: // LLL
fmtWX += "%b"; fmtWX += "%b";
break; break;
case 4: // LLLL case 4: // LLLL
fmtWX += "%B"; fmtWX += "%B";
break; break;
case 5: // LLLLL case 5: // LLLLL
// no "narrow form" in strftime(), use abbrev. // no "narrow form" in strftime(), use abbrev.
fmtWX += "%b"; fmtWX += "%b";
break; break;
default: default:
wxFAIL_MSG( "too many 'L's" ); wxFAIL_MSG( "too many 'L's" );
} }
break; break;
#endif #endif
case 'M': case 'M':
@@ -1578,38 +1234,6 @@ LCTYPE wxGetLCTYPEFormatFromLocalInfo(wxLocaleInfo index)
return 0; return 0;
} }
namespace
{
// This private function additionally checks consistency of the decimal
// separator settings between MSW and CRT.
wxString
GetInfoFromLCID(LCID lcid, wxLocaleInfo index, wxLocaleCategory cat)
{
const wxString str = wxGetInfoFromLCID(lcid, index, cat);
if ( !str.empty() && index == wxLOCALE_DECIMAL_POINT )
{
// As we get our decimal point separator from Win32 and not the
// CRT there is a possibility of mismatch between them and this
// can easily happen if the user code called setlocale()
// instead of using wxLocale to change the locale. And this can
// result in very strange bugs elsewhere in the code as the
// assumptions that formatted strings do use the decimal
// separator actually fail, so check for it here.
wxASSERT_MSG
(
wxString::Format("%.3f", 1.23).find(str) != wxString::npos,
"Decimal separator mismatch -- did you use setlocale()?"
"If so, use wxLocale to change the locale instead."
);
}
return str;
}
} // anonymous namespace
// This function is also used by wxUILocaleImpl, so don't make it private. // This function is also used by wxUILocaleImpl, so don't make it private.
wxString wxString
wxGetInfoFromLCID(LCID lcid, wxLocaleInfo index, wxLocaleCategory cat) wxGetInfoFromLCID(LCID lcid, wxLocaleInfo index, wxLocaleCategory cat)
@@ -1680,35 +1304,13 @@ wxGetInfoFromLCID(LCID lcid, wxLocaleInfo index, wxLocaleCategory cat)
/* static */ /* static */
wxString wxLocale::GetInfo(wxLocaleInfo index, wxLocaleCategory cat) wxString wxLocale::GetInfo(wxLocaleInfo index, wxLocaleCategory cat)
{ {
if ( !wxGetLocale() ) return wxUILocale::GetCurrent().GetInfo(index, cat);
{
// wxSetLocale() hadn't been called yet of failed, hence CRT must be
// using "C" locale -- but check it to detect bugs that would happen if
// this were not the case.
wxASSERT_MSG( strcmp(setlocale(LC_ALL, NULL), "C") == 0,
wxS("You probably called setlocale() directly instead ")
wxS("of using wxLocale and now there is a ")
wxS("mismatch between C/C++ and Windows locale.\n")
wxS("Things are going to break, please only change ")
wxS("locale by creating wxLocale objects to avoid this!") );
// Return the hard coded values for C locale. This is really the right
// thing to do as there is no LCID we can use in the code below in this
// case, even LOCALE_INVARIANT is not quite the same as C locale (the
// only difference is that it uses %Y instead of %y in the date format
// but this difference is significant enough).
return wxGetStdCLocaleInfo(index, cat);
}
// wxSetLocale() succeeded and so thread locale was set together with CRT one.
return GetInfoFromLCID(::GetThreadLocale(), index, cat);
} }
/* static */ /* static */
wxString wxLocale::GetOSInfo(wxLocaleInfo index, wxLocaleCategory cat) wxString wxLocale::GetOSInfo(wxLocaleInfo index, wxLocaleCategory cat)
{ {
return GetInfoFromLCID(::GetThreadLocale(), index, cat); return wxUILocale::GetCurrent().GetInfo(index, cat);
} }
#elif defined(__WXOSX__) #elif defined(__WXOSX__)

View File

@@ -23,6 +23,7 @@
#include "wx/uilocale.h" #include "wx/uilocale.h"
#include "wx/arrstr.h" #include "wx/arrstr.h"
#include "wx/intl.h"
#ifndef __WINDOWS__ #ifndef __WINDOWS__
#include "wx/language.h" #include "wx/language.h"
@@ -30,6 +31,35 @@
#include "wx/private/uilocale.h" #include "wx/private/uilocale.h"
// ----------------------------------------------------------------------------
// helper functions
// ----------------------------------------------------------------------------
namespace
{
const char* validCharsAlpha = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
const char* validCharsAlnum = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
const char* validCharsModExt = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-";
const char* validCharsTag = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.@";
// Handle special case "ca-ES-valencia"
// Sync attributes modifier and extension
inline void CheckLanguageVariant(wxLocaleIdent& locId)
{
if (locId.GetModifier().IsSameAs("valencia"))
{
locId.Extension(locId.GetModifier());
}
else if (locId.GetExtension().IsSameAs("valencia") && locId.GetModifier().empty())
{
locId.Modifier(locId.GetExtension());
}
}
} // anonymous namespace
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// global variables // global variables
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@@ -50,27 +80,97 @@ wxUILocale wxUILocale::ms_current;
/* static */ /* static */
wxLocaleIdent wxLocaleIdent::FromTag(const wxString& tag) wxLocaleIdent wxLocaleIdent::FromTag(const wxString& tag)
{ {
// This method accepts tags in various formats: BCP47, Windows, POSIX, and macOS.
//
// See section 2.01 of https://www.rfc-editor.org/rfc/bcp/bcp47.txt for the // See section 2.01 of https://www.rfc-editor.org/rfc/bcp/bcp47.txt for the
// full syntax. Here we fully support just the subset we're interested in: // full syntax. Here we fully support just the subset we're interested in:
// //
// - Normal language tags (not private use or grandfathered ones). // - Normal language tags (not private use or grandfathered ones).
// - Only script and region, but not the extensions or extlangs. // - Script and region.
//
// Additionally platform-specific tags are supported:
// - Extensions (without validity checks) (Windows only).
// - Charset and modifier (POSIX only)
//
// Only language, script, and region are supported across all platforms.
// The script tag is mapped to the modifier for POSIX platforms.
// The script tag takes precedence, if a modifier is also specified.
//
// The following tag syntax is accepted:
// BCP47: language[-script][-region][-extension]
// Windows: language[-script][-region][-extension][_sortorder]
// POSIX: language[_region][.charset][@modifier]
// macOS: language[-script][_region]
// Language tags must always use ASCII. // Locale tags must always use alphanumeric characters and certain special characters "-_.@"
if ( tag != tag.ToAscii() ) if (tag.find_first_not_of(validCharsTag) != wxString::npos)
{
wxFAIL_MSG("tag contains invalid characters (not alphanumeric) or invalid delimiters");
return wxLocaleIdent(); return wxLocaleIdent();
}
const wxArrayString& parts = wxSplit(tag, '-', '\0'); wxLocaleIdent locId;
// 1. Handle platform-dependent cases
// 1a. Check for modifier in POSIX tag
wxString tagRest;
wxString tagMain = tag.BeforeFirst('@', &tagRest);
if (!tagRest.empty())
{
// POSIX modifier found
wxString script = wxUILocale::GetScriptNameFromAlias(tagRest);
if (!script.empty())
locId.Script(script);
else
locId.Modifier(tagRest);
}
else
{
wxASSERT_MSG(tag.find('@') == wxString::npos, "modifier delimiter found, but modifier is empty");
}
// 1b. Check for charset in POSIX tag
tagMain = tagMain.BeforeFirst('.', &tagRest);
if (!tagRest.empty())
{
// POSIX charset found
locId.Charset(tagRest);
}
else
{
wxASSERT_MSG(tagMain.find('.') == wxString::npos, "charset delimiter found, but charset is empty");
}
// 1c. Check for sort order in Windows tag
//
// Make sure we don't extract the region identifier erroneously as a sortorder identifier
wxString tagTemp = tagMain.BeforeFirst('_', &tagRest);
if (tagRest.length() > 4 && locId.m_modifier.empty() && locId.m_charset.empty())
{
// Windows sortorder found
locId.SortOrder(tagRest);
tagMain = tagTemp;
}
// 2. Handle remaining tag identifier as being BCP47-like
// Now that special POSIX attributes have been handled
// POSIX specific delimiters must no longer be present
wxASSERT_MSG(tagMain.find_first_of(".@") == wxString::npos, "tag contains invalid delimiters");
// Replace '_' separators by '-' to simplify further processing
tagMain.Replace("_", "-");
const wxArrayString& parts = wxSplit(tagMain, '-', '\0');
wxArrayString::const_iterator it = parts.begin(); wxArrayString::const_iterator it = parts.begin();
if ( it == parts.end() ) if ( it == parts.end() )
return wxLocaleIdent(); return wxLocaleIdent();
// We have at least the language, so we'll return a valid object. // We have at least the language, so we'll return a valid object.
wxLocaleIdent locId; locId.m_language = (*it).Lower();
locId.m_language = *it;
// Also store the full string, so that the platforms that support BCP 47 // Also store the full string.
// natively can use it instead of reconstructing the string from our fields.
locId.m_tag = tag; locId.m_tag = tag;
if ( ++it == parts.end() ) if ( ++it == parts.end() )
@@ -103,82 +203,209 @@ wxLocaleIdent wxLocaleIdent::FromTag(const wxString& tag)
case 2: case 2:
case 3: case 3:
// Either an ISO 3166-1 or UN M.49 region code. // Either an ISO 3166-1 or UN M.49 region code.
locId.m_region = *it; locId.m_region = (*it).Upper();
break; break;
case 4: case 4:
// Must be an ISO 15924 script. // Must be an ISO 15924 script.
locId.m_script = *it; wxASSERT_MSG(locId.m_script.empty(), "script already set");
locId.m_script = (*it).Left(1).Upper() + (*it).Mid(2).Lower();
break; break;
default: default:
// This looks to be completely invalid. // This looks to be completely invalid.
wxFAIL_MSG("invalid code length, script or region code expected");
return wxLocaleIdent(); return wxLocaleIdent();
} }
// If we got the language and the region, we can't parse anything else // Check whether we have got the region above.
// (variants, extensions, private use) anyhow. // If not, we must have got the script. So, check if we have the region, too.
if ( !locId.m_region.empty() ) if (++it == parts.end())
return locId;
// Otherwise we must have got the script above, so check if we have the
// region too.
if ( ++it == parts.end() )
return locId;
switch ( it->length() )
{ {
case 2: CheckLanguageVariant(locId);
case 3: return locId;
locId.m_region = *it;
break;
} }
if (locId.m_region.empty())
{
switch (it->length())
{
case 2:
case 3:
locId.m_region = (*it).Upper(); ++it;
break;
}
}
// If there is still anything to parse (variants, extensions, private use),
// we assign it to the extension.
if (it != parts.end())
{
wxString custom = *it;
while (++it != parts.end())
{
custom << "-" << *it;
}
locId.m_extension = custom;
}
// We also handle the only language variant known at the time of writing:
// valencia (ca-ES-valencia resp ca_ES@valencia).
CheckLanguageVariant(locId);
return locId; return locId;
} }
wxLocaleIdent& wxLocaleIdent::Language(const wxString& language) wxLocaleIdent& wxLocaleIdent::Language(const wxString& language)
{ {
m_language = language; if (language.IsSameAs("C", false) || language.IsSameAs("POSIX", false))
{
m_language = language.Upper();
}
else if ((language.length() == 2 || language.length() == 3) &&
language.find_first_not_of(validCharsAlpha) == wxString::npos)
{
m_language = language.Lower();
}
else
{
m_language.clear();
}
return *this; return *this;
} }
wxLocaleIdent& wxLocaleIdent::Region(const wxString& region) wxLocaleIdent& wxLocaleIdent::Region(const wxString& region)
{ {
m_region = region; if ((region.length() == 2 || region.length() == 3) &&
region.find_first_not_of(validCharsAlnum) == wxString::npos)
{
m_region = region.Upper();
}
else
{
m_region.clear();
}
return *this; return *this;
} }
wxLocaleIdent& wxLocaleIdent::Script(const wxString& script) wxLocaleIdent& wxLocaleIdent::Script(const wxString& script)
{ {
m_script = script; if (script.length() == 4 &&
script.find_first_not_of(validCharsAlpha) == wxString::npos)
{
// Capitalize first character
m_script = script.Left(1).Upper() + script.Mid(2).Lower();
}
else if (!script.empty())
{
m_script = wxUILocale::GetScriptNameFromAlias(script.Lower());
}
else
{
m_script.clear();
}
return *this; return *this;
} }
wxLocaleIdent& wxLocaleIdent::Charset(const wxString& charset) wxLocaleIdent& wxLocaleIdent::Charset(const wxString& charset)
{ {
m_charset = charset; if (charset.find_first_not_of(validCharsModExt) == wxString::npos)
{
m_charset = charset;
}
else
{
m_charset.clear();
}
return *this; return *this;
} }
wxLocaleIdent& wxLocaleIdent::Modifier(const wxString& modifier) wxLocaleIdent& wxLocaleIdent::Modifier(const wxString& modifier)
{ {
m_modifier = modifier; if (modifier.find_first_not_of(validCharsModExt) == wxString::npos)
{
m_modifier = modifier;
}
else
{
m_modifier.clear();
}
return *this; return *this;
} }
wxString wxLocaleIdent::GetTag() const wxLocaleIdent& wxLocaleIdent::Extension(const wxString& extension)
{ {
if ( !m_tag.empty() ) // Windows extensions follow the BCP 47 syntax
if (extension.find_first_not_of(validCharsModExt) == wxString::npos)
{
m_extension = extension;
}
return *this;
}
wxLocaleIdent& wxLocaleIdent::SortOrder(const wxString& sortorder)
{
// Windows sortorder identifiers all seem to have a length of 6 characters.
// To distinguish sortorder from script and region identifiers require length > 4.
if (sortorder.length() > 4 &&
sortorder.find_first_not_of(validCharsAlpha) == wxString::npos)
{
m_sortorder = sortorder;
}
return *this;
}
wxString wxLocaleIdent::GetTag(wxLocaleTagType tagType) const
{
if (tagType == wxLOCALE_TAGTYPE_DEFAULT && !m_tag.empty() )
return m_tag; return m_tag;
wxString tag = m_language; wxString tag = m_language;
switch (tagType)
{
case wxLOCALE_TAGTYPE_BCP47:
if (!m_script.empty())
tag << '-' << m_script;
if (!m_region.empty())
tag << '-' << m_region;
if (!m_extension.empty())
tag << '-' << m_extension;
break;
if ( !m_script.empty() ) case wxLOCALE_TAGTYPE_MACOS:
tag << '-' << m_script; if (!m_script.empty())
tag << '-' << m_script;
if (!m_region.empty())
tag << '_' << m_region;
break;
if ( !m_region.empty() ) case wxLOCALE_TAGTYPE_POSIX:
tag << '-' << m_region; if (!m_region.empty())
tag << '_' << m_region;
if (!m_charset.empty())
tag << '.' << m_charset;
if (!m_script.empty())
tag << '@' << wxUILocale::GetScriptAliasFromName(m_script);
else if (!m_modifier.empty())
tag << '@' << m_modifier;
break;
case wxLOCALE_TAGTYPE_WINDOWS:
if (!m_script.empty())
tag << '-' << m_script;
if (!m_region.empty())
tag << '-' << m_region;
if (!m_extension.empty())
tag << '-' << m_extension;
if (!m_sortorder.empty())
tag << '-' << m_sortorder;
break;
case wxLOCALE_TAGTYPE_SYSTEM:
default:
tag = GetName();
break;
}
return tag; return tag;
} }
@@ -240,10 +467,31 @@ bool wxUILocale::UseDefault()
} }
/* static */ /* static */
bool wxUILocale::UseLanguage(const wxLanguageInfo& info) bool wxUILocale::UseLocaleName(const wxString& localeName)
{ {
wxUILocaleImpl* const impl = wxUILocaleImpl::CreateForLanguage(info); wxUILocaleImpl* impl = NULL;
if ( !impl ) if (localeName.IsSameAs("C", false) || localeName.IsSameAs("POSIX", false))
{
impl = wxUILocaleImpl::CreateStdC();
}
else
{
wxLocaleIdent localeId = wxLocaleIdent::FromTag(localeName);
impl = wxUILocaleImpl::CreateForLocale(localeId);
if (!impl)
{
// Creating the locale may have failed due to lacking support for wxUILocaleImplName
// Try to locate the locale in our language database
const wxLanguageInfo* const info = wxUILocale::FindLanguageInfo(localeId);
if (info)
{
// Language found in language database
// Try to create a locale based on the language
impl = wxUILocaleImpl::CreateForLanguage(*info);
}
}
}
if (!impl)
return false; return false;
impl->Use(); impl->Use();
@@ -308,6 +556,14 @@ wxString wxUILocale::GetName() const
return m_impl->GetName(); return m_impl->GetName();
} }
wxLocaleIdent wxUILocale::GetLocaleId() const
{
if (!m_impl)
return wxLocaleIdent();
return m_impl->GetLocaleId();
}
wxString wxUILocale::GetInfo(wxLocaleInfo index, wxLocaleCategory cat) const wxString wxUILocale::GetInfo(wxLocaleInfo index, wxLocaleCategory cat) const
{ {
if ( !m_impl ) if ( !m_impl )
@@ -316,6 +572,35 @@ wxString wxUILocale::GetInfo(wxLocaleInfo index, wxLocaleCategory cat) const
return m_impl->GetInfo(index, cat); return m_impl->GetInfo(index, cat);
} }
wxString wxUILocale::GetLocalizedName(wxLocaleName name, wxLocaleForm form) const
{
if (!m_impl)
return wxString();
return m_impl->GetLocalizedName(name, form);
}
wxLayoutDirection wxUILocale::GetLayoutDirection() const
{
if (!m_impl)
return wxLayout_Default;
wxLayoutDirection dir = m_impl->GetLayoutDirection();
if (dir == wxLayout_Default)
{
wxLocaleIdent localeId = m_impl->GetLocaleId();
if (!localeId.IsEmpty())
{
const wxLanguageInfo* const info = wxUILocale::FindLanguageInfo(localeId);
if (info)
{
dir = info->LayoutDirection;
}
}
}
return dir;
}
int int
wxUILocale::CompareStrings(const wxString& lhs, wxUILocale::CompareStrings(const wxString& lhs,
const wxString& rhs, const wxString& rhs,
@@ -341,4 +626,180 @@ wxUILocale::~wxUILocale()
m_impl->DecRef(); m_impl->DecRef();
} }
/*static*/
int wxUILocale::GetSystemLanguage()
{
const wxLanguageInfos& languagesDB = wxGetLanguageInfos();
size_t count = languagesDB.size();
wxVector<wxString> preferred = wxUILocaleImpl::GetPreferredUILanguages();
for (wxVector<wxString>::const_iterator j = preferred.begin();
j != preferred.end();
++j)
{
wxLocaleIdent localeId = wxLocaleIdent::FromTag(*j);
wxString lang = localeId.GetTag(wxLOCALE_TAGTYPE_BCP47);
size_t pos = lang.find('-');
wxString langShort = (pos != wxString::npos) ? lang.substr(0, pos) : wxString();
size_t ixShort = count;
for (size_t ixLanguage = 0; ixLanguage < count; ++ixLanguage)
{
if (languagesDB[ixLanguage].LocaleTag == lang)
{
return languagesDB[ixLanguage].Language;
}
if (pos != wxString::npos)
{
if (languagesDB[ixLanguage].LocaleTag == lang)
{
ixShort = ixLanguage;
}
}
}
if (ixShort < count)
{
return languagesDB[ixShort].Language;
}
}
// no info about this language in the database
return wxLANGUAGE_UNKNOWN;
}
/* static */
wxVector<wxString> wxUILocale::GetPreferredUILanguages()
{
return wxUILocaleImpl::GetPreferredUILanguages();
}
/* static */
const wxLanguageInfo* wxUILocale::GetLanguageInfo(int lang)
{
CreateLanguagesDB();
// calling GetLanguageInfo(wxLANGUAGE_DEFAULT) is a natural thing to do, so
// make it work
if (lang == wxLANGUAGE_DEFAULT)
lang = GetSystemLanguage();
if (lang == wxLANGUAGE_UNKNOWN)
return NULL;
const wxLanguageInfos& languagesDB = wxGetLanguageInfos();
const size_t count = languagesDB.size();
for (size_t i = 0; i < count; i++)
{
if (languagesDB[i].Language == lang)
return &languagesDB[i];
}
return NULL;
}
/* static */
wxString wxUILocale::GetLanguageName(int lang)
{
wxString string;
if (lang == wxLANGUAGE_DEFAULT || lang == wxLANGUAGE_UNKNOWN)
return string;
const wxLanguageInfo* info = GetLanguageInfo(lang);
if (info)
string = info->Description;
return string;
}
/* static */
wxString wxUILocale::GetLanguageCanonicalName(int lang)
{
wxString string;
if (lang == wxLANGUAGE_DEFAULT || lang == wxLANGUAGE_UNKNOWN)
return string;
const wxLanguageInfo* info = GetLanguageInfo(lang);
if (info)
string = info->CanonicalName;
return string;
}
/* static */
const wxLanguageInfo* wxUILocale::FindLanguageInfo(const wxString& locale)
{
CreateLanguagesDB();
const wxLanguageInfo* infoRet = NULL;
const wxLanguageInfos& languagesDB = wxGetLanguageInfos();
const size_t count = languagesDB.size();
for (size_t i = 0; i < count; i++)
{
const wxLanguageInfo* info = &languagesDB[i];
if (wxStricmp(locale, info->CanonicalName) == 0 ||
wxStricmp(locale, info->Description) == 0)
{
// exact match, stop searching
infoRet = info;
break;
}
if (wxStricmp(locale, info->CanonicalName.BeforeFirst(wxS('_'))) == 0)
{
// a match -- but maybe we'll find an exact one later, so continue
// looking
//
// OTOH, maybe we had already found a language match and in this
// case don't overwrite it because the entry for the default
// country always appears first in gs_languagesDB
if (!infoRet)
infoRet = info;
}
}
return infoRet;
}
/* static */
const wxLanguageInfo* wxUILocale::FindLanguageInfo(const wxLocaleIdent& locId)
{
CreateLanguagesDB();
const wxLanguageInfo* infoRet = NULL;
wxString localeTag = locId.GetTag(wxLOCALE_TAGTYPE_BCP47);
const wxLanguageInfos& languagesDB = wxGetLanguageInfos();
const size_t count = languagesDB.size();
for (size_t i = 0; i < count; i++)
{
const wxLanguageInfo* info = &languagesDB[i];
if (wxStricmp(localeTag, info->LocaleTag) == 0)
{
// exact match, stop searching
infoRet = info;
break;
}
if (wxStricmp(localeTag, info->LocaleTag.BeforeFirst(wxS('-'))) == 0)
{
// a match -- but maybe we'll find an exact one later, so continue
// looking
//
// OTOH, maybe we had already found a language match and in this
// case don't overwrite it because the entry for the default
// country always appears first in gs_languagesDB
if (!infoRet)
infoRet = info;
}
}
return infoRet;
}
#endif // wxUSE_INTL #endif // wxUSE_INTL

View File

@@ -25,6 +25,7 @@
#include "wx/msw/private/uilocale.h" #include "wx/msw/private/uilocale.h"
#include "wx/scopedarray.h"
#include "wx/dynlib.h" #include "wx/dynlib.h"
#ifndef LOCALE_NAME_USER_DEFAULT #ifndef LOCALE_NAME_USER_DEFAULT
@@ -35,6 +36,42 @@
#define MUI_LANGUAGE_NAME 8 #define MUI_LANGUAGE_NAME 8
#endif #endif
#ifndef LOCALE_ICONSTRUCTEDLOCALE
#define LOCALE_ICONSTRUCTEDLOCALE 0x0000007d
#endif
#ifndef LOCALE_RETURN_NUMBER
#define LOCALE_RETURN_NUMBER 0x20000000
#endif
#ifndef LOCALE_SENGLISHDISPLAYNAME
#define LOCALE_SENGLISHDISPLAYNAME 0x00000072
#endif
#ifndef LOCALE_SNATIVEDISPLAYNAME
#define LOCALE_SNATIVEDISPLAYNAME 0x00000073
#endif
#ifndef LOCALE_SENGLISHLANGUAGENAME
#define LOCALE_SENGLISHLANGUAGENAME 0x00001001
#endif
#ifndef LOCALE_SNATIVELANGUAGENAME
#define LOCALE_SNATIVELANGUAGENAME 0x00000004
#endif
#ifndef LOCALE_SENGLISHCOUNTRYNAME
#define LOCALE_SENGLISHCOUNTRYNAME 0x00001002
#endif
#ifndef LOCALE_SNATIVECOUNTRYNAME
#define LOCALE_SNATIVECOUNTRYNAME 0x00000008
#endif
#ifndef LOCALE_IREADINGLAYOUT
#define LOCALE_IREADINGLAYOUT 0x00000070
#endif
// ============================================================================ // ============================================================================
// implementation // implementation
// ============================================================================ // ============================================================================
@@ -96,11 +133,105 @@ wxString wxLocaleIdent::GetName() const
{ {
name << "-" << m_region; name << "-" << m_region;
} }
if (!m_extension.empty())
{
name << "-" << m_extension;
}
if (!m_sortorder.empty())
{
name << "_" << m_sortorder;
}
} }
return name; return name;
} }
// ----------------------------------------------------------------------------
// Standard C wxUILocale implementation for MSW
// ----------------------------------------------------------------------------
class wxUILocaleImplStdC : public wxUILocaleImpl
{
public:
// Create object corresponding to the given locale, return NULL if not
// supported.
static wxUILocaleImplStdC* Create()
{
return new wxUILocaleImplStdC();
}
~wxUILocaleImplStdC() wxOVERRIDE
{
}
void Use() wxOVERRIDE
{
}
wxString GetName() const wxOVERRIDE
{
return wxString("C");
}
wxLocaleIdent GetLocaleId() const wxOVERRIDE
{
return wxLocaleIdent().Language("C");
}
wxString GetInfo(wxLocaleInfo index, wxLocaleCategory cat) const wxOVERRIDE
{
return wxGetStdCLocaleInfo(index, cat);
}
wxString GetLocalizedName(wxLocaleName name, wxLocaleForm WXUNUSED(form)) const wxOVERRIDE
{
wxString str;
switch (name)
{
case wxLOCALE_NAME_LOCALE:
case wxLOCALE_NAME_LANGUAGE:
str = wxString("English");
break;
case wxLOCALE_NAME_COUNTRY:
str = wxString();
break;
default:
wxFAIL_MSG("unknown wxLocaleInfo");
}
return str;
}
wxLayoutDirection GetLayoutDirection() const wxOVERRIDE
{
return wxLayout_Default;
}
int CompareStrings(const wxString& lhs, const wxString& rhs,
int flags) const wxOVERRIDE
{
const int rc = flags & wxCompare_CaseInsensitive ? lhs.CmpNoCase(rhs)
: lhs.Cmp(rhs);
if (rc < 0)
return -1;
if (rc > 0)
return 1;
return 0;
}
private:
// Ctor is private, use Create() instead.
explicit wxUILocaleImplStdC()
{
}
wxDECLARE_NO_COPY_CLASS(wxUILocaleImplStdC);
};
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// LCID-based wxUILocale implementation for MSW // LCID-based wxUILocale implementation for MSW
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@@ -121,23 +252,27 @@ public:
wxString GetName() const wxOVERRIDE wxString GetName() const wxOVERRIDE
{ {
wxChar buf[256]; wxString str;
buf[0] = wxT('\0');
// Try using newer constant available since Vista which produces names // Try using newer constant available since Vista which produces names
// more similar to the other platforms. // more similar to the other platforms.
if ( wxGetWinVersion() >= wxWinVersion_Vista ) if ( wxGetWinVersion() >= wxWinVersion_Vista )
{ {
::GetLocaleInfo(m_lcid, LOCALE_SNAME, buf, WXSIZEOF(buf)); str = DoGetInfo(LOCALE_SNAME);
} }
else // TODO-XP: Drop this branch. else // TODO-XP: Drop this branch.
{ {
// This name constant is available under all systems, including // This name constant is available under all systems, including
// pre-Vista ones. // pre-Vista ones.
::GetLocaleInfo(m_lcid, LOCALE_SENGLANGUAGE, buf, WXSIZEOF(buf)); str = DoGetInfo(LOCALE_SENGLANGUAGE);
} }
return buf; return str;
}
wxLocaleIdent GetLocaleId() const wxOVERRIDE
{
return wxLocaleIdent::FromTag(GetName());
} }
wxString GetInfo(wxLocaleInfo index, wxLocaleCategory cat) const wxOVERRIDE wxString GetInfo(wxLocaleInfo index, wxLocaleCategory cat) const wxOVERRIDE
@@ -145,6 +280,68 @@ public:
return wxGetInfoFromLCID(m_lcid, index, cat); return wxGetInfoFromLCID(m_lcid, index, cat);
} }
wxString GetLocalizedName(wxLocaleName name, wxLocaleForm form) const wxOVERRIDE
{
wxString str;
switch (name)
{
case wxLOCALE_NAME_LOCALE:
switch (form)
{
case wxLOCALE_FORM_NATIVE:
{
wxString strLang = DoGetInfo(LOCALE_SNATIVELANGNAME);
wxString strCtry = DoGetInfo(LOCALE_SNATIVECTRYNAME);
str << strLang << " (" << strCtry << ")";
}
break;
case wxLOCALE_FORM_ENGLISH:
{
wxString strLang = DoGetInfo(LOCALE_SENGLANGUAGE);
wxString strCtry = DoGetInfo(LOCALE_SENGCOUNTRY);
str << strLang << " (" << strCtry << ")";
}
break;
default:
wxFAIL_MSG("unknown wxLocaleForm");
}
break;
case wxLOCALE_NAME_LANGUAGE:
switch (form)
{
case wxLOCALE_FORM_NATIVE:
str = DoGetInfo(LOCALE_SNATIVELANGNAME);
break;
case wxLOCALE_FORM_ENGLISH:
str = DoGetInfo(LOCALE_SENGLANGUAGE);
break;
default:
wxFAIL_MSG("unknown wxLocaleForm");
}
break;
case wxLOCALE_NAME_COUNTRY:
switch (form)
{
case wxLOCALE_FORM_NATIVE:
str = DoGetInfo(LOCALE_SNATIVECTRYNAME);
break;
case wxLOCALE_FORM_ENGLISH:
str = DoGetInfo(LOCALE_SENGCOUNTRY);
break;
default:
wxFAIL_MSG("unknown wxLocaleForm");
}
break;
}
return str;
}
wxLayoutDirection GetLayoutDirection() const wxOVERRIDE
{
return wxLayout_Default;
}
int CompareStrings(const wxString& lhs, const wxString& rhs, int CompareStrings(const wxString& lhs, const wxString& rhs,
int flags) const wxOVERRIDE int flags) const wxOVERRIDE
{ {
@@ -158,6 +355,18 @@ public:
private: private:
const LCID m_lcid; const LCID m_lcid;
wxString DoGetInfo(LCTYPE lctype) const
{
wchar_t buf[256];
if (!::GetLocaleInfo(m_lcid, lctype, buf, WXSIZEOF(buf)))
{
wxLogLastError(wxT("GetLocaleInfo"));
return wxString();
}
return buf;
}
wxDECLARE_NO_COPY_CLASS(wxUILocaleImplLCID); wxDECLARE_NO_COPY_CLASS(wxUILocaleImplLCID);
}; };
@@ -186,6 +395,9 @@ public:
wxDL_INIT_FUNC(ms_, SetThreadPreferredUILanguages, dllKernel32); wxDL_INIT_FUNC(ms_, SetThreadPreferredUILanguages, dllKernel32);
if ( !ms_SetThreadPreferredUILanguages ) if ( !ms_SetThreadPreferredUILanguages )
return false; return false;
wxDL_INIT_FUNC(ms_, GetUserPreferredUILanguages, dllKernel32);
if (!ms_GetUserPreferredUILanguages)
return false;
wxDL_INIT_FUNC(ms_, CompareStringEx, dllKernel32); wxDL_INIT_FUNC(ms_, CompareStringEx, dllKernel32);
if ( !ms_CompareStringEx ) if ( !ms_CompareStringEx )
@@ -197,6 +409,66 @@ public:
return s_canUse == 1; return s_canUse == 1;
} }
static wxVector<wxString> GetPreferredUILanguages()
{
wxVector<wxString> preferred;
if (CanUse())
{
ULONG numberOfLanguages = 0;
ULONG bufferSize = 0;
if (ms_GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &numberOfLanguages, NULL, &bufferSize))
{
wxScopedArray<WCHAR> languages(bufferSize);
if (ms_GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &numberOfLanguages, languages.get(), &bufferSize))
{
WCHAR* buf = languages.get();
for (unsigned k = 0; k < numberOfLanguages; ++k)
{
const wxString language(buf);
preferred.push_back(language);
buf += language.length() + 1;
}
}
else
{
wxLogLastError(wxT("GetUserPreferredUILanguages"));
}
}
else
{
wxLogLastError(wxT("GetUserPreferredUILanguages"));
}
}
else
{
const wxLanguageInfos& languagesDB = wxGetLanguageInfos();
size_t count = languagesDB.size();
const LANGID langid = ::GetUserDefaultUILanguage();
if (langid != LOCALE_CUSTOM_UI_DEFAULT)
{
size_t ixLanguage = 0;
wxUint32 lang = PRIMARYLANGID(langid);
wxUint32 sublang = SUBLANGID(langid);
for (ixLanguage = 0; ixLanguage < count; ++ixLanguage)
{
if (languagesDB[ixLanguage].WinLang == lang &&
languagesDB[ixLanguage].WinSublang == sublang)
{
break;
}
}
if (ixLanguage < count)
{
preferred.push_back(languagesDB[ixLanguage].CanonicalName);
}
}
}
return preferred;
}
// Create object corresponding to the default user locale. // Create object corresponding to the default user locale.
static wxUILocaleImplName* CreateDefault() static wxUILocaleImplName* CreateDefault()
{ {
@@ -212,6 +484,37 @@ public:
if ( !ms_GetLocaleInfoEx(name, LOCALE_SNAME, NULL, 0) ) if ( !ms_GetLocaleInfoEx(name, LOCALE_SNAME, NULL, 0) )
return NULL; return NULL;
// Unfortunately under Windows 10 the call above only fails if the given
// locale name is not a valid BCP 47 identifier. For example,
// valid language codes can have 2 or 3 letters:
// - using name "w" fails
// - using name "wx" succeeds
// - using name "wxy" succeeds
// - using name "wxyz" fails
// and so we need another check on these systems (but note that this
// check must not be done under Windows 7 because there plenty of
// actually supported locales are "constructed") by checking
// whether the locale is "constructed" or not: "not constructed"
// means the locale is a predefined locale, "constructed"
// means the locale is not predefined, but has to be constructed.
// For example, "de-US" would be a constructed locale.
if ( wxGetWinVersion() >= wxWinVersion_10 )
{
// Using LOCALE_ICONSTRUCTEDLOCALE to query the locale status is
// discouraged by Microsoft (the constant is not even defined in a
// Windows header file). However, for now constructed locales will
// be rejected here.
int isConstructed = 0;
if (!ms_GetLocaleInfoEx
(
name,
LOCALE_ICONSTRUCTEDLOCALE | LOCALE_RETURN_NUMBER,
(LPWSTR)&isConstructed,
sizeof(int)
) || isConstructed != 0)
return NULL;
}
return new wxUILocaleImplName(name); return new wxUILocaleImplName(name);
} }
@@ -242,6 +545,11 @@ public:
return DoGetInfo(LOCALE_SNAME); return DoGetInfo(LOCALE_SNAME);
} }
wxLocaleIdent GetLocaleId() const wxOVERRIDE
{
return wxLocaleIdent::FromTag(GetName());
}
wxString GetInfo(wxLocaleInfo index, wxLocaleCategory cat) const wxOVERRIDE wxString GetInfo(wxLocaleInfo index, wxLocaleCategory cat) const wxOVERRIDE
{ {
// TODO-XP: This duplicates code from in wxGetInfoFromLCID(), but // TODO-XP: This duplicates code from in wxGetInfoFromLCID(), but
@@ -286,6 +594,93 @@ public:
return str; return str;
} }
wxString GetLocalizedName(wxLocaleName name, wxLocaleForm form) const wxOVERRIDE
{
// TODO-XP: This duplicates code from in wxGetInfoFromLCID(), but
// it's only temporary because we will drop all code using LCID soon.
// LOCALE_SINTLSYMBOL (Currency 3-letter ISO 4217)
static wxWinVersion ver = wxGetWinVersion();
wxString str;
switch (name)
{
case wxLOCALE_NAME_LOCALE:
switch (form)
{
case wxLOCALE_FORM_NATIVE:
if (ver >= wxWinVersion_7)
{
str = DoGetInfo(LOCALE_SNATIVEDISPLAYNAME);
}
else
{
wxString strLang = DoGetInfo(LOCALE_SNATIVELANGNAME);
wxString strCtry = DoGetInfo(LOCALE_SNATIVECTRYNAME);
str << strLang << " (" << strCtry << ")";
}
break;
case wxLOCALE_FORM_ENGLISH:
if (ver >= wxWinVersion_7)
{
str = DoGetInfo(LOCALE_SENGLISHDISPLAYNAME);
}
else
{
wxString strLang = DoGetInfo(LOCALE_SENGLANGUAGE);
wxString strCtry = DoGetInfo(LOCALE_SENGCOUNTRY);
str << strLang << " (" << strCtry << ")";
}
break;
default:
wxFAIL_MSG("unknown wxLocaleForm");
}
break;
case wxLOCALE_NAME_LANGUAGE:
switch (form)
{
case wxLOCALE_FORM_NATIVE:
str = DoGetInfo((ver >= wxWinVersion_7) ? LOCALE_SNATIVELANGUAGENAME : LOCALE_SNATIVELANGNAME);
break;
case wxLOCALE_FORM_ENGLISH:
str = DoGetInfo((ver >= wxWinVersion_7) ? LOCALE_SENGLISHLANGUAGENAME : LOCALE_SENGLANGUAGE);
break;
default:
wxFAIL_MSG("unknown wxLocaleForm");
}
break;
case wxLOCALE_NAME_COUNTRY:
switch (form)
{
case wxLOCALE_FORM_NATIVE:
str = DoGetInfo((ver >= wxWinVersion_7) ? LOCALE_SNATIVECOUNTRYNAME : LOCALE_SNATIVECTRYNAME);
break;
case wxLOCALE_FORM_ENGLISH:
str = DoGetInfo((ver >= wxWinVersion_7) ? LOCALE_SENGLISHCOUNTRYNAME : LOCALE_SENGCOUNTRY);
break;
default:
wxFAIL_MSG("unknown wxLocaleForm");
}
break;
}
return str;
}
wxLayoutDirection GetLayoutDirection() const wxOVERRIDE
{
if (wxGetWinVersion() >= wxWinVersion_7)
{
wxString str = DoGetInfo(LOCALE_IREADINGLAYOUT);
// str contains a number between 0 and 3:
// 0 = LTR, 1 = RTL, 2 = TTB+RTL, 3 = TTB + LTR
// If str equals 1 return RTL, otherwise LTR
return (str.IsSameAs("1") ? wxLayout_RightToLeft : wxLayout_LeftToRight);
}
else
{
return wxLayout_Default;
}
}
int CompareStrings(const wxString& lhs, const wxString& rhs, int CompareStrings(const wxString& lhs, const wxString& rhs,
int flags) const wxOVERRIDE int flags) const wxOVERRIDE
{ {
@@ -326,6 +721,9 @@ private:
typedef BOOL (WINAPI *SetThreadPreferredUILanguages_t)(DWORD, CONST WCHAR*, ULONG*); typedef BOOL (WINAPI *SetThreadPreferredUILanguages_t)(DWORD, CONST WCHAR*, ULONG*);
static SetThreadPreferredUILanguages_t ms_SetThreadPreferredUILanguages; static SetThreadPreferredUILanguages_t ms_SetThreadPreferredUILanguages;
typedef BOOL (WINAPI *GetUserPreferredUILanguages_t)(DWORD, ULONG*, WCHAR*, ULONG*);
static GetUserPreferredUILanguages_t ms_GetUserPreferredUILanguages;
// Note: we currently don't use NLSVERSIONINFO output parameter and so we // Note: we currently don't use NLSVERSIONINFO output parameter and so we
// don't bother dealing with the different sizes of this struct under // don't bother dealing with the different sizes of this struct under
// different OS versions and define the function type as using "void*" to // different OS versions and define the function type as using "void*" to
@@ -367,6 +765,7 @@ private:
wxUILocaleImplName::GetLocaleInfoEx_t wxUILocaleImplName::ms_GetLocaleInfoEx; wxUILocaleImplName::GetLocaleInfoEx_t wxUILocaleImplName::ms_GetLocaleInfoEx;
wxUILocaleImplName::SetThreadPreferredUILanguages_t wxUILocaleImplName::ms_SetThreadPreferredUILanguages; wxUILocaleImplName::SetThreadPreferredUILanguages_t wxUILocaleImplName::ms_SetThreadPreferredUILanguages;
wxUILocaleImplName::GetUserPreferredUILanguages_t wxUILocaleImplName::ms_GetUserPreferredUILanguages;
wxUILocaleImplName::CompareStringEx_t wxUILocaleImplName::ms_CompareStringEx; wxUILocaleImplName::CompareStringEx_t wxUILocaleImplName::ms_CompareStringEx;
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@@ -376,14 +775,7 @@ wxUILocaleImplName::CompareStringEx_t wxUILocaleImplName::ms_CompareStringEx;
/* static */ /* static */
wxUILocaleImpl* wxUILocaleImpl::CreateStdC() wxUILocaleImpl* wxUILocaleImpl::CreateStdC()
{ {
if ( !wxUILocaleImplName::CanUse() ) return wxUILocaleImplStdC::Create();
{
// There is no LCID for "C" locale, but US English is basically the same.
LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
return new wxUILocaleImplLCID(lcid);
}
return wxUILocaleImplName::Create(L"en-US");
} }
/* static */ /* static */
@@ -398,14 +790,19 @@ wxUILocaleImpl* wxUILocaleImpl::CreateUserDefault()
/* static */ /* static */
wxUILocaleImpl* wxUILocaleImpl::CreateForLanguage(const wxLanguageInfo& info) wxUILocaleImpl* wxUILocaleImpl::CreateForLanguage(const wxLanguageInfo& info)
{ {
if ( info.WinLang == 0 ) if (!wxUILocaleImplName::CanUse())
{ {
wxLogWarning(wxS("Locale '%s' not supported by OS."), info.Description); if (info.WinLang == 0)
{
return NULL; wxLogWarning(wxS("Locale '%s' not supported by OS."), info.Description);
return NULL;
}
return new wxUILocaleImplLCID(info.GetLCID());
}
else
{
return wxUILocaleImplName::Create(info.LocaleTag.wc_str());
} }
return new wxUILocaleImplLCID(info.GetLCID());
} }
/* static */ /* static */
@@ -420,7 +817,13 @@ wxUILocaleImpl* wxUILocaleImpl::CreateForLocale(const wxLocaleIdent& locId)
return NULL; return NULL;
} }
return wxUILocaleImplName::Create(locId.GetTag().wc_str()); return wxUILocaleImplName::Create(locId.GetTag(wxLOCALE_TAGTYPE_WINDOWS).wc_str());
}
/* static */
wxVector<wxString> wxUILocaleImpl::GetPreferredUILanguages()
{
return wxUILocaleImplName::GetPreferredUILanguages();
} }
#endif // wxUSE_INTL #endif // wxUSE_INTL

View File

@@ -101,7 +101,10 @@ public:
void Use() wxOVERRIDE; void Use() wxOVERRIDE;
wxString GetName() const wxOVERRIDE; wxString GetName() const wxOVERRIDE;
wxLocaleIdent GetLocaleId() const wxOVERRIDE;
wxString GetInfo(wxLocaleInfo index, wxLocaleCategory cat) const wxOVERRIDE; wxString GetInfo(wxLocaleInfo index, wxLocaleCategory cat) const wxOVERRIDE;
wxString GetLocalizedName(wxLocaleName name, wxLocaleForm form) const wxOVERRIDE;
wxLayoutDirection GetLayoutDirection() const wxOVERRIDE;
int CompareStrings(const wxString& lhs, const wxString& rhs, int CompareStrings(const wxString& lhs, const wxString& rhs,
int flags) const wxOVERRIDE; int flags) const wxOVERRIDE;
@@ -130,12 +133,78 @@ wxUILocaleImplCF::GetName() const
return wxCFStringRef::AsString([m_nsloc localeIdentifier]); return wxCFStringRef::AsString([m_nsloc localeIdentifier]);
} }
wxLocaleIdent
wxUILocaleImplCF::GetLocaleId() const
{
return wxLocaleIdent::FromTag(GetName());
}
wxString wxString
wxUILocaleImplCF::GetInfo(wxLocaleInfo index, wxLocaleCategory cat) const wxUILocaleImplCF::GetInfo(wxLocaleInfo index, wxLocaleCategory cat) const
{ {
return wxGetInfoFromCFLocale((CFLocaleRef)m_nsloc, index, cat); return wxGetInfoFromCFLocale((CFLocaleRef)m_nsloc, index, cat);
} }
wxString
wxUILocaleImplCF::GetLocalizedName(wxLocaleName name, wxLocaleForm form) const
{
NSString* str = NULL;
switch (name)
{
case wxLOCALE_NAME_LOCALE:
switch (form)
{
case wxLOCALE_FORM_NATIVE:
str = [m_nsloc localizedStringForLocaleIdentifier:[m_nsloc localeIdentifier]];
break;
case wxLOCALE_FORM_ENGLISH:
str = [m_nsloc displayNameForKey:NSLocaleIdentifier value:[m_nsloc localeIdentifier]];
break;
default:
wxFAIL_MSG("unknown wxLocaleForm");
}
break;
case wxLOCALE_NAME_LANGUAGE:
switch (form)
{
case wxLOCALE_FORM_NATIVE:
str = [m_nsloc localizedStringForLanguageCode:[m_nsloc languageCode]];
break;
case wxLOCALE_FORM_ENGLISH:
str = [m_nsloc displayNameForKey:NSLocaleIdentifier value:[m_nsloc localeIdentifier]];
break;
default:
wxFAIL_MSG("unknown wxLocaleForm");
}
break;
case wxLOCALE_NAME_COUNTRY:
switch (form)
{
case wxLOCALE_FORM_NATIVE:
str = [m_nsloc localizedStringForCountryCode:[m_nsloc countryCode]];
break;
case wxLOCALE_FORM_ENGLISH:
str = [m_nsloc displayNameForKey:NSLocaleIdentifier value:[m_nsloc localeIdentifier]];
break;
default:
wxFAIL_MSG("unknown wxLocaleForm");
}
break;
}
return wxCFStringRef::AsString(str);
}
wxLayoutDirection
wxUILocaleImplCF::GetLayoutDirection() const
{
NSLocaleLanguageDirection layoutDirection = [NSLocale characterDirectionForLanguage:[m_nsloc languageCode]];
if (layoutDirection == NSLocaleLanguageDirectionLeftToRight)
return wxLayout_LeftToRight;
else if (layoutDirection == NSLocaleLanguageDirectionRightToLeft)
return wxLayout_RightToLeft;
return wxLayout_Default;
}
/* static */ /* static */
wxUILocaleImpl* wxUILocaleImpl::CreateStdC() wxUILocaleImpl* wxUILocaleImpl::CreateStdC()
{ {
@@ -154,6 +223,19 @@ wxUILocaleImpl* wxUILocaleImpl::CreateForLocale(const wxLocaleIdent& locId)
return wxUILocaleImplCF::Create(locId); return wxUILocaleImplCF::Create(locId);
} }
/* static */
wxVector<wxString> wxUILocaleImpl::GetPreferredUILanguages()
{
wxVector<wxString> preferred;
NSArray* preferredLangs = [NSLocale preferredLanguages];
NSUInteger count = preferredLangs.count;
for (NSUInteger j = 0; j < count; ++j)
preferred.push_back(wxCFStringRef::AsString(preferredLangs[j]));
return preferred;
}
int int
wxUILocaleImplCF::CompareStrings(const wxString& lhs, const wxString& rhs, wxUILocaleImplCF::CompareStrings(const wxString& lhs, const wxString& rhs,
int flags) const int flags) const

View File

@@ -26,6 +26,7 @@
#include "wx/unix/private/uilocale.h" #include "wx/unix/private/uilocale.h"
#include "wx/intl.h" #include "wx/intl.h"
#include "wx/utils.h"
#include <locale.h> #include <locale.h>
#ifdef HAVE_LANGINFO_H #ifdef HAVE_LANGINFO_H
@@ -35,6 +36,13 @@
namespace namespace
{ {
// Small helper function: get the value of the given environment variable and
// return true only if the variable was found and has non-empty value.
inline bool wxGetNonEmptyEnvVar(const wxString& name, wxString* value)
{
return wxGetEnv(name, value) && !value->empty();
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// wxUILocale implementation using standard Unix/C functions // wxUILocale implementation using standard Unix/C functions
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@@ -53,7 +61,11 @@ public:
void Use() wxOVERRIDE; void Use() wxOVERRIDE;
wxString GetName() const wxOVERRIDE; wxString GetName() const wxOVERRIDE;
wxLocaleIdent GetLocaleId() const wxOVERRIDE;
wxString GetInfo(wxLocaleInfo index, wxLocaleCategory cat) const wxOVERRIDE; wxString GetInfo(wxLocaleInfo index, wxLocaleCategory cat) const wxOVERRIDE;
wxString GetLocalizedName(wxLocaleName name, wxLocaleForm form) const wxOVERRIDE;
wxLayoutDirection GetLayoutDirection() const wxOVERRIDE;
int CompareStrings(const wxString& lhs, const wxString& rhs, int CompareStrings(const wxString& lhs, const wxString& rhs,
int flags) const wxOVERRIDE; int flags) const wxOVERRIDE;
@@ -64,6 +76,7 @@ private:
#endif // HAVE_LANGINFO_H #endif // HAVE_LANGINFO_H
wxLocaleIdent m_locId; wxLocaleIdent m_locId;
wxString m_codeset;
#ifdef HAVE_LOCALE_T #ifdef HAVE_LOCALE_T
// Only null for the default locale. // Only null for the default locale.
@@ -85,15 +98,39 @@ inline locale_t TryCreateLocale(const wxLocaleIdent& locId)
// modifying its wxLocaleIdent argument if it succeeds). // modifying its wxLocaleIdent argument if it succeeds).
locale_t TryCreateLocaleWithUTF8(wxLocaleIdent& locId) locale_t TryCreateLocaleWithUTF8(wxLocaleIdent& locId)
{ {
locale_t loc = TryCreateLocale(locId); locale_t loc = NULL;
if ( !loc && locId.GetCharset().empty() )
#if wxUSE_UNICODE
if ( locId.GetCharset().empty() )
{ {
wxLocaleIdent locIdUTF8 = wxLocaleIdent(locId).Charset("UTF-8"); wxLocaleIdent locIdUTF8(locId);
locIdUTF8.Charset(wxS("UTF-8"));
loc = TryCreateLocale(locIdUTF8); loc = TryCreateLocale(locIdUTF8);
if ( !loc )
{
locIdUTF8.Charset(wxS("utf-8"));
loc = TryCreateLocale(locIdUTF8);
}
if ( !loc )
{
locIdUTF8.Charset(wxS("UTF8"));
loc = TryCreateLocale(locIdUTF8);
}
if ( !loc )
{
locIdUTF8.Charset(wxS("utf8"));
loc = TryCreateLocale(locIdUTF8);
}
if ( loc ) if ( loc )
locId = locIdUTF8; locId = locIdUTF8;
} }
// if we can't set UTF-8 locale, try non-UTF-8 one:
if ( !loc )
#endif // wxUSE_UNICODE
loc = TryCreateLocale(locId);
return loc; return loc;
} }
@@ -107,38 +144,35 @@ locale_t TryCreateMatchingLocale(wxLocaleIdent& locId)
// Try to find a variant of this locale available on this system: first // Try to find a variant of this locale available on this system: first
// of all, using just the language, without the territory, typically // of all, using just the language, without the territory, typically
// does _not_ work under Linux, so try adding one if we don't have it. // does _not_ work under Linux, so try adding one if we don't have it.
if ( locId.GetRegion().empty() ) const wxString lang = locId.GetLanguage();
const wxLanguageInfos& infos = wxGetLanguageInfos();
for ( wxLanguageInfos::const_iterator it = infos.begin();
it != infos.end();
++it )
{ {
const wxString lang = locId.GetLanguage(); const wxString& fullname = it->CanonicalRef.empty() ? it->CanonicalName : it->CanonicalRef;
if ( fullname.BeforeFirst('_') == lang )
const wxLanguageInfos& infos = wxGetLanguageInfos();
for ( wxLanguageInfos::const_iterator it = infos.begin();
it != infos.end();
++it )
{ {
const wxString& fullname = it->CanonicalName; // We never have encoding in our canonical names, but we
if ( fullname.BeforeFirst('_') == lang ) // can have modifiers, so get rid of them if necessary.
const wxString&
region = fullname.AfterFirst('_').BeforeFirst('@');
if ( !region.empty() )
{ {
// We never have encoding in our canonical names, but we loc = TryCreateLocaleWithUTF8(locId.Region(region));
// can have modifiers, so get rid of them if necessary. if ( loc )
const wxString&
region = fullname.AfterFirst('_').BeforeFirst('@');
if ( !region.empty() )
{ {
loc = TryCreateLocaleWithUTF8(locId.Region(region)); // We take the first available region, we don't
if ( loc ) // have enough data to know how to prioritize them
{ // (and wouldn't want to start any geopolitical
// We take the first available region, we don't // disputes).
// have enough data to know how to prioritize them break;
// (and wouldn't want to start any geopolitical
// disputes).
break;
}
} }
// Don't bother reverting region to the old value as it will
// be overwritten during the next loop iteration anyhow.
} }
// Don't bother reverting region to the old value as it will
// be overwritten during the next loop iteration anyhow.
} }
} }
} }
@@ -170,7 +204,9 @@ wxString wxLocaleIdent::GetName() const
if ( !m_charset.empty() ) if ( !m_charset.empty() )
name << "." << m_charset; name << "." << m_charset;
if ( !m_modifier.empty() ) if ( !m_script.empty() )
name << "@" << wxUILocale::GetScriptAliasFromName(m_script);
else if ( !m_modifier.empty() )
name << "@" << m_modifier; name << "@" << m_modifier;
} }
@@ -189,22 +225,22 @@ static const char *wxSetlocaleTryUTF8(int c, const wxLocaleIdent& locId)
if ( locId.GetCharset().empty() ) if ( locId.GetCharset().empty() )
{ {
wxLocaleIdent locIdUTF8(locId); wxLocaleIdent locIdUTF8(locId);
locIdUTF8.Charset(wxS(".UTF-8")); locIdUTF8.Charset(wxS("UTF-8"));
l = wxSetlocale(c, locIdUTF8.GetName()); l = wxSetlocale(c, locIdUTF8.GetName());
if ( !l ) if ( !l )
{ {
locIdUTF8.Charset(wxS(".utf-8")); locIdUTF8.Charset(wxS("utf-8"));
l = wxSetlocale(c, locIdUTF8.GetName()); l = wxSetlocale(c, locIdUTF8.GetName());
} }
if ( !l ) if ( !l )
{ {
locIdUTF8.Charset(wxS(".UTF8")); locIdUTF8.Charset(wxS("UTF8"));
l = wxSetlocale(c, locIdUTF8.GetName()); l = wxSetlocale(c, locIdUTF8.GetName());
} }
if ( !l ) if ( !l )
{ {
locIdUTF8.Charset(wxS(".utf8")); locIdUTF8.Charset(wxS("utf8"));
l = wxSetlocale(c, locIdUTF8.GetName()); l = wxSetlocale(c, locIdUTF8.GetName());
} }
} }
@@ -245,6 +281,11 @@ wxUILocaleImplUnix::wxUILocaleImplUnix(wxLocaleIdent locId
, m_locale(loc) , m_locale(loc)
#endif // HAVE_LOCALE_T #endif // HAVE_LOCALE_T
{ {
#ifdef HAVE_LANGINFO_H
m_codeset = GetLangInfo(CODESET);
#else
m_codeset = "";
#endif // HAVE_LANGINFO_H
} }
wxUILocaleImplUnix::~wxUILocaleImplUnix() wxUILocaleImplUnix::~wxUILocaleImplUnix()
@@ -292,7 +333,20 @@ wxUILocaleImplUnix::Use()
wxString wxString
wxUILocaleImplUnix::GetName() const wxUILocaleImplUnix::GetName() const
{ {
return m_locId.GetName(); wxString name = m_locId.GetName();
if (name.empty())
{
char* rv = setlocale(LC_ALL, NULL);
if (rv)
name = wxString::FromUTF8(rv);
}
return name;
}
wxLocaleIdent
wxUILocaleImplUnix::GetLocaleId() const
{
return m_locId;
} }
#ifdef HAVE_LANGINFO_H #ifdef HAVE_LANGINFO_H
@@ -360,6 +414,86 @@ wxUILocaleImplUnix::GetInfo(wxLocaleInfo index, wxLocaleCategory cat) const
#endif // HAVE_LANGINFO_H/!HAVE_LANGINFO_H #endif // HAVE_LANGINFO_H/!HAVE_LANGINFO_H
} }
wxString
wxUILocaleImplUnix::GetLocalizedName(wxLocaleName name, wxLocaleForm form) const
{
#ifdef HAVE_LANGINFO_H
wxString str;
switch (name)
{
case wxLOCALE_NAME_LOCALE:
switch (form)
{
case wxLOCALE_FORM_NATIVE:
{
str = wxString(GetLangInfo(_NL_ADDRESS_LANG_NAME), wxCSConv(m_codeset));
wxString strCtry = wxString(GetLangInfo(_NL_ADDRESS_COUNTRY_NAME), wxCSConv(m_codeset));
if (!strCtry.empty())
{
str << " (" << strCtry << ")";
}
}
break;
case wxLOCALE_FORM_ENGLISH:
{
str = wxString(GetLangInfo(_NL_IDENTIFICATION_LANGUAGE), wxCSConv(m_codeset));
wxString strCtry = wxString(GetLangInfo(_NL_IDENTIFICATION_TERRITORY), wxCSConv(m_codeset));
if (!strCtry.empty())
{
str << " (" << strCtry << ")";
}
}
break;
default:
wxFAIL_MSG("unknown wxLocaleForm");
}
break;
case wxLOCALE_NAME_LANGUAGE:
switch (form)
{
case wxLOCALE_FORM_NATIVE:
str = wxString(GetLangInfo(_NL_ADDRESS_LANG_NAME), wxCSConv(m_codeset));
break;
case wxLOCALE_FORM_ENGLISH:
str = wxString(GetLangInfo(_NL_IDENTIFICATION_LANGUAGE), wxCSConv(m_codeset));
break;
default:
wxFAIL_MSG("unknown wxLocaleForm");
}
break;
case wxLOCALE_NAME_COUNTRY:
switch (form)
{
case wxLOCALE_FORM_NATIVE:
str = wxString(GetLangInfo(_NL_ADDRESS_COUNTRY_NAME), wxCSConv(m_codeset));
break;
case wxLOCALE_FORM_ENGLISH:
str = wxString(GetLangInfo(_NL_IDENTIFICATION_TERRITORY), wxCSConv(m_codeset));
break;
default:
wxFAIL_MSG("unknown wxLocaleForm");
}
default:
wxFAIL_MSG("unknown wxLocaleName");
}
return str;
#else // !HAVE_LANGINFO_H
// If HAVE_LANGINFO_H is not available, we could use our own language database
// to retrieve the requested information.
// For now, just return an empty string.
return wxString();
#endif // HAVE_LANGINFO_H/!HAVE_LANGINFO_H
}
wxLayoutDirection
wxUILocaleImplUnix::GetLayoutDirection() const
{
// Under Linux/Unix the locale data do not contain information
// about layout direction. For now, return wxLayout_Default.
// wxUILocale will try to use the language database as a fallback.
return wxLayout_Default;
}
int int
wxUILocaleImplUnix::CompareStrings(const wxString& lhs, const wxString& rhs, wxUILocaleImplUnix::CompareStrings(const wxString& lhs, const wxString& rhs,
int WXUNUSED(flags)) const int WXUNUSED(flags)) const
@@ -413,4 +547,127 @@ wxUILocaleImpl* wxUILocaleImpl::CreateForLocale(const wxLocaleIdent& locIdOrig)
#endif // HAVE_LOCALE_T/!HAVE_LOCALE_T #endif // HAVE_LOCALE_T/!HAVE_LOCALE_T
} }
/* static */
wxVector<wxString> wxUILocaleImpl::GetPreferredUILanguages()
{
wxVector<wxString> preferred;
// first get the string identifying the language from the environment
wxString langFull;
if (!wxGetNonEmptyEnvVar(wxS("LC_ALL"), &langFull) &&
!wxGetNonEmptyEnvVar(wxS("LC_MESSAGES"), &langFull) &&
!wxGetNonEmptyEnvVar(wxS("LANG"), &langFull))
{
// no language specified, treat it as English
preferred.push_back("en-US");
return preferred;
}
// the language string has the following form
//
// lang[_LANG][.encoding][@modifier]
//
// (see environ(5) in the Open Unix specification)
//
// where lang is the primary language, LANG is a sublang/territory,
// encoding is the charset to use and modifier "allows the user to select
// a specific instance of localization data within a single category"
//
// for example, the following strings are valid:
// fr
// fr_FR
// de_DE.iso88591
// de_DE@euro
// de_DE.iso88591@euro
// for now we don't use the encoding, although we probably should (doing
// translations of the msg catalogs on the fly as required) (TODO)
//
// we need the modifier for languages like Valencian: ca_ES@valencia
// though, remember it
wxString modifier;
size_t posModifier = langFull.find_first_of(wxS("@"));
if (posModifier != wxString::npos)
modifier = langFull.Mid(posModifier);
size_t posEndLang = langFull.find_first_of(wxS("@."));
if (posEndLang != wxString::npos)
{
langFull.Truncate(posEndLang);
}
if (langFull == wxS("C") || langFull == wxS("POSIX"))
{
// default C locale is English too
preferred.push_back("en_US");
return preferred;
}
// do we have just the language (or sublang too)?
const bool justLang = langFull.find('_') == wxString::npos;
if (justLang && langFull.length() > 2)
{
const wxLanguageInfos& languagesDB = wxGetLanguageInfos();
size_t count = languagesDB.size();
// In addition to the format above, we also can have full language
// names in LANG env var - for example, SuSE is known to use
// LANG="german" - so check for use of non-standard format and try to
// find the name in verbose description.
for (size_t i = 0; i < count; i++)
{
if (languagesDB[i].Description.CmpNoCase(langFull) == 0)
{
break;
}
if (i < count)
langFull = languagesDB[i].CanonicalName;
}
}
// 0. Make sure the lang is according to latest ISO 639
// (this is necessary because glibc uses iw and in instead
// of he and id respectively).
// the language itself (second part is the region)
wxString langOrig = ExtractLang(langFull);
wxString region = ExtractNotLang(langFull);
wxString lang;
if (langOrig == wxS("iw"))
lang = wxS("he");
else if (langOrig == wxS("in"))
lang = wxS("id");
else if (langOrig == wxS("ji"))
lang = wxS("yi");
else if (langOrig == wxS("no") && region == wxS("_NO"))
lang = wxS("nb");
else if (langOrig == wxS("no") && region == wxS("_NY"))
{
lang = wxS("nn");
region = wxS("_NO");
}
else if (langOrig == wxS("no"))
lang = wxS("nb");
else
lang = langOrig;
// did we change it?
if (lang != langOrig)
{
langFull = lang + region;
}
if (!modifier.empty())
{
// Locale name with modifier
preferred.push_back(langFull + modifier);
}
// Locale name without modifier
preferred.push_back(langFull);
return preferred;
}
#endif // wxUSE_INTL #endif // wxUSE_INTL

View File

@@ -262,7 +262,8 @@ TEST_CASE("wxUILocale::IsSupported", "[uilocale]")
{ {
CheckSupported(wxUILocale::FromTag("en"), "English"); CheckSupported(wxUILocale::FromTag("en"), "English");
CheckSupported(wxUILocale(wxLocaleIdent().Language("fr").Region("FR")), "French"); CheckSupported(wxUILocale(wxLocaleIdent().Language("fr").Region("FR")), "French");
CHECK( !wxUILocale::FromTag("bloordyblop").IsSupported() ); CHECK_FALSE( wxUILocale::FromTag("bloordyblop").IsSupported() );
CHECK_FALSE( wxUILocale::FromTag("xyz").IsSupported() );
} }
TEST_CASE("wxUILocale::GetInfo", "[uilocale]") TEST_CASE("wxUILocale::GetInfo", "[uilocale]")