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
int m_language; // this locale wxLanguage value
wxString m_oldUILocale; // previous wxUILocale name
const char *m_pszOldLocale; // previous locale from setlocale()
wxLocale *m_pOldLocale; // previous wxLocale
#ifdef __WIN32__

View File

@@ -32,6 +32,31 @@ enum wxLayoutDirection
// 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
{
// (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.
// translation information

View File

@@ -63,6 +63,11 @@ public:
// work, so it just falls back on CreateStdC() if it fails to create it.
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.
//
// This is not implemented for all platforms, notably not for Mac where the
@@ -72,7 +77,10 @@ public:
// Functions corresponding to wxUILocale ones.
virtual wxString GetName() const = 0;
virtual wxLocaleIdent GetLocaleId() 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,
int flags) const = 0;

View File

@@ -16,6 +16,7 @@
#include "wx/localedefs.h"
#include "wx/string.h"
#include "wx/vector.h"
class wxUILocaleImpl;
@@ -56,12 +57,20 @@ public:
// Set modifier (only supported under Unix)
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.
const wxString& GetLanguage() const { return m_language; }
const wxString& GetRegion() const { return m_region; }
const wxString& GetScript() const { return m_script; }
const wxString& GetCharset() const { return m_charset; }
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
wxString GetName() const;
@@ -69,7 +78,7 @@ public:
// Get the language tag: for the objects created with FromTag() returns the
// string passed to it directly, otherwise reconstructs this string from
// the components.
wxString GetTag() const;
wxString GetTag(wxLocaleTagType tagType = wxLOCALE_TAGTYPE_DEFAULT) const;
// Empty locale identifier is invalid. at least Language() must be called.
bool IsEmpty() const
@@ -85,6 +94,8 @@ private:
wxString m_script;
wxString m_charset;
wxString m_modifier;
wxString m_extension;
wxString m_sortorder;
};
// ----------------------------------------------------------------------------
@@ -97,11 +108,11 @@ public:
// Configure the UI to use the default user locale.
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
// 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.
static const wxUILocale& GetCurrent();
@@ -126,10 +137,19 @@ public:
// Get the platform-dependent name of the current locale.
wxString GetName() const;
// Get the locale id from which the current locale was instantiated.
wxLocaleIdent GetLocaleId() const;
// Query the locale for the specified information.
wxString GetInfo(wxLocaleInfo index,
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.
int CompareStrings(const wxString& lhs, const wxString& rhs,
int flags = wxCompare_CaseSensitive) const;
@@ -142,6 +162,10 @@ public:
// Return wxLANGUAGE_UNKNOWN if language-guessing algorithm failed
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
//
// 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
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.
// Notes: 1) wxLanguageInfo contains platform-specific data
// 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.
@@ -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

View File

@@ -162,6 +162,11 @@ public:
*/
wxString GetName() const;
/**
Get the locale id from which the current locale was instantiated.
*/
wxLocaleIdent GetLocaleId() const;
/**
Query the locale for the specified information.
@@ -179,6 +184,26 @@ public:
wxString GetInfo(wxLocaleInfo index,
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.
@@ -210,6 +235,18 @@ public:
*/
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
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:
- 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
the subset of BCP 47 (https://www.rfc-editor.org/rfc/bcp/bcp47.txt)
syntax.
@@ -308,7 +345,39 @@ class wxLocaleIdent
{
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
letters) and may contain script and region separated by dashes, i.e.
@@ -363,7 +432,9 @@ public:
/**
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.
@@ -391,15 +462,46 @@ public:
Note that this value is only used under Unix systems and simply ignored
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.
@param modifier
Modifier is defined by ISO/IEC 15897.
It is a semi-colon separated list of identifiers, or name=value pairs.
Modifier is a free-form text string.
*/
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.
const wxString& GetLanguage() const;
@@ -415,15 +517,36 @@ public:
/// Return the modifier part of the locale identifier.
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.
Format:
Windows: \<language\>-\<script\>-\<REGION\>
Unix: \<language\>_\<REGION\>.\<charset\>@\<modifier\>
Windows: \<language\>-\<script\>-\<REGION\>-\<extension\>_\<sortorder\>
Unix: \<language\>_\<REGION\>.\<charset\>@\{\<modifier\>\|\<scriptalias\>\}
MacOS: \<language\>-\<script\>_\<REGION\>
*/
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.

Binary file not shown.

View File

@@ -5,8 +5,8 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: 2021-11-11 23:05+0100\n"
"PO-Revision-Date: 2021-11-11 23:09+0100\n"
"POT-Creation-Date: 2021-12-10 10:06+0100\n"
"PO-Revision-Date: 2021-12-10 10:15+0100\n"
"Last-Translator: Ulrich Telle <ulrich@telle-online.eu>\n"
"Language-Team: \n"
"Language: de\n"
@@ -103,37 +103,37 @@ msgstr "Testet die Übersetzung der Schaltflächen im Hinweisfenster"
#: internat.cpp:316
msgid "item"
msgstr "Eintrag"
msgstr "Eintrag ohne Kontext"
#: internat.cpp:317
msgctxt "context_1"
msgid "item"
msgstr "Eintrag A"
msgstr "Eintrag Kontext 1"
#: internat.cpp:318
msgctxt "context_2"
msgid "item"
msgstr "Eintrag B"
msgstr "Eintrag Kontext 2"
#: internat.cpp:319 internat.cpp:320
msgid "sing"
msgid_plural "plur"
msgstr[0] "Einzahl"
msgstr[1] "Mehrzahl"
msgstr[0] "Einzahl ohne Kontext"
msgstr[1] "Mehrzahl ohne Kontext"
#: internat.cpp:321 internat.cpp:322
msgctxt "context_1"
msgid "sing"
msgid_plural "plur"
msgstr[0] "Einzahl A"
msgstr[1] "Mehrzahl A"
msgstr[0] "Einzahl Kontext 1"
msgstr[1] "Mehrzahl Kontext 1"
#: internat.cpp:323 internat.cpp:324
msgctxt "context_2"
msgid "sing"
msgid_plural "plur"
msgstr[0] "Einzahl B"
msgstr[1] "Mehrzahl B"
msgstr[0] "Einzahl Kontext 2"
msgstr[1] "Mehrzahl Kontext 2"
#: internat.cpp:328
msgid "Show coreutils &help"
@@ -161,24 +161,34 @@ msgstr "&Hilfe"
msgid "Current UI locale: %s; C locale: %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:"
msgstr "Numerische Eingabe:"
#: internat.cpp:393
#: internat.cpp:417
msgid "Number"
msgstr "Zahl"
#: internat.cpp:397
#: internat.cpp:421
msgid "Date"
msgstr "Datum"
#: internat.cpp:413
#: internat.cpp:437
#, c-format
msgid "Number in UI locale: %s; in C locale: %.2f"
msgstr "Zahl im UI Gebietsschema: %s; im C Gebietsschema: %.2f"
#: internat.cpp:433
#: internat.cpp:457
msgid ""
"I18n sample\n"
"(c) 1998, 1999 Vadim Zeitlin and Julian Smart"
@@ -186,108 +196,111 @@ msgstr ""
"I18n Beispiel\n"
"© 1998, 1999 Vadim Zeitlin und Julian Smart"
#: internat.cpp:434
#: internat.cpp:458
msgid "About Internat"
msgstr "Über Internat"
#: internat.cpp:467
#: internat.cpp:491
msgid "Enter your number:"
msgstr "Geben Sie eine Zahl ein:"
#: internat.cpp:468
#: internat.cpp:492
msgid "Try to guess my number!"
msgstr "Versuchen Sie, die Zahl zu raten!"
#: internat.cpp:482
#: internat.cpp:506
msgid "You've probably entered an invalid number."
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:"
msgstr ""
"Herzlichen Glückwunsch! Sie haben gewonnen. Hier ist der magische Satz:"
#: internat.cpp:497
#: internat.cpp:521
#, c-format
msgid "cannot create fifo `%s'"
msgstr "Fifo `%s' kann nicht erzeugt werden"
#: internat.cpp:506
#: internat.cpp:530
msgid "Bad luck! try again..."
msgstr "Pech gehabt! Versuchen Sie es noch einmal..."
#: internat.cpp:514
#: internat.cpp:538
msgid "Result"
msgstr "Ergebnis"
#: internat.cpp:522
#: internat.cpp:546
msgid "Enter the locale to test"
msgstr "Geben Sie das zu testende Gebietsschema ein"
#: internat.cpp:534
#: internat.cpp:562
#, c-format
msgid "Locale \"%s\" is unknown."
msgstr "Gebietsschema \"%s\" ist unbekannt."
msgid ""
"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
#, c-format
msgid "Locale \"%s\" is available."
msgstr "Gebietsschema \"%s\" ist verfügbar."
#: internat.cpp:544
#: internat.cpp:569
#, c-format
msgid "Locale \"%s\" is not available."
msgstr "Gebietsschema \"%s\" ist nicht verfügbar."
#: internat.cpp:559
#: internat.cpp:584
msgid "Dummy file dialog"
msgstr "Dummy-Datei-Dialog"
#: internat.cpp:564
#: internat.cpp:589
msgid "Testing _() (gettext)"
msgstr "Test von _() (gettext)"
#: internat.cpp:572
#: internat.cpp:597
msgid "Please enter text to translate"
msgstr "Bitte geben Sie einen zu übersetzenden Text ein"
#: internat.cpp:573
#: internat.cpp:598
msgid "default value"
msgstr "Default-Wert"
#: internat.cpp:587
#: internat.cpp:612
msgid "Testing _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"
msgstr ""
"Bitte geben Sie einen Bereich für Pluralformen des Satzes \"n Dateien "
"gelöscht\" ein"
#: internat.cpp:601
#: internat.cpp:626
msgid "file deleted"
msgid_plural "files deleted"
msgstr[0] "Datei gelöscht"
msgstr[1] "Dateien gelöscht"
#: internat.cpp:612
#: internat.cpp:637
msgid "line 1"
msgstr "Zeile 1"
#: internat.cpp:613
#: internat.cpp:638
msgid "line 2"
msgstr "Zeile 2"
#: internat.cpp:614
#: internat.cpp:639
msgid "line 3"
msgstr "Zeile 3"
#: internat.cpp:617
#: internat.cpp:642
msgid "Testing wxTRANSLATE() (gettext_noop)"
msgstr "Test von wxTRANSLATE() (gettext_noop)"
#: internat.cpp:630
#: internat.cpp:655
msgid ""
"Are the labels of the buttons in this message box translated into the "
"current locale language?"
@@ -295,10 +308,18 @@ msgstr ""
"Wurden die Beschriftungen der Schaltflächen in diesem Hinweisfenster in die "
"Sprache des aktuellen Gebietsschemas übersetzt?"
#: internat.cpp:632
#: internat.cpp:657
msgid "wxWidgets i18n sample"
msgstr "wxWidgets I18n Beispiel"
#: internat.cpp:637
#: internat.cpp:662
msgid "Please report the details of your platform to us."
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 ""
msgstr ""
"Project-Id-Version: wxWindows 2.0 i18n sample\n"
"POT-Creation-Date: 1999-01-13 18:19+0100\n"
"PO-Revision-Date: 2016-09-20 19:38+0200\n"
"Last-Translator: Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>\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"
"POT-Creation-Date: 2021-12-10 10:11+0100\n"
"PO-Revision-Date: 2021-12-10 10:12+0100\n"
"Last-Translator: Ulrich Telle <ulrich@telle-online.eu>\n"
"Language-Team: \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"
msgstr "Item without context"
#: internat.cpp:317
msgctxt "context_1"
msgid "item"
msgstr "Item in context 1"
#: internat.cpp:318
msgctxt "context_2"
msgid "item"
msgstr "Item in context 2"
#: internat.cpp:319 internat.cpp:320
msgid "sing"
msgid_plural "plur"
msgstr[0] "Singular form without context"
msgstr[1] "Plural form without context"
#: internat.cpp:321 internat.cpp:322
msgctxt "context_1"
msgid "sing"
msgid_plural "plur"
msgstr[0] "Singular form in context 1"
msgstr[1] "Plural form in context 1"
#: internat.cpp:323 internat.cpp:324
msgctxt "context_2"
msgid "sing"
msgid_plural "plur"
msgstr[0] "Singular form in context 2"
msgstr[1] "Plural form in context 2"
#: internat.cpp:98
msgid "International wxWindows App"
msgstr "Application wxWindows internationale"
#: internat.cpp:328
msgid "Show coreutils &help"
msgstr ""
#: internat.cpp:105
#: internat.cpp:331
msgid "&About"
msgstr "&A propos"
#: internat.cpp:107
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
#: internat.cpp:336
msgid "&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 ""
"I18n sample\n"
"© 1998, 1999 Vadim Zeitlin and Julian Smart"
"(c) 1998, 1999 Vadim Zeitlin and Julian Smart"
msgstr ""
"Exemple d'i18n\n"
"© 1998, 1999 Vadim Zeitlin et Julian Smart"
#: internat.cpp:139
#: internat.cpp:458
msgid "About Internat"
msgstr "A propos d'Internat"
#: internat.cpp:144
#: internat.cpp:491
msgid "Enter your number:"
msgstr "Entrez votre numéro:"
#: internat.cpp:145
#: internat.cpp:492
msgid "Try to guess my number!"
msgstr "Essayez de déviner mon numéro!"
#: internat.cpp:150
#: internat.cpp:506
msgid "You've probably entered an invalid number."
msgstr "Vous avez probablement entré un nombre invalide."
#: internat.cpp:154
msgid "Bad luck! try again..."
msgstr "Pas de chance! essayez encore..."
#: internat.cpp:158
#: internat.cpp:520
msgid "Congratulations! you've won. Here is the magic phrase:"
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"
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());
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
// this demonstrates RTL layout mirroring for Arabic locales and using
@@ -528,16 +552,17 @@ void MyFrame::OnTestLocaleAvail(wxCommandEvent& WXUNUSED(event))
return;
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
{

View File

@@ -166,24 +166,14 @@ const char* wxLanguageInfo::TrySetLocale() const
wxString wxLanguageInfo::GetLocaleName() const
{
const char* const orig = wxSetlocale(LC_ALL, NULL);
const char* const ret = TrySetLocale();
wxString retval;
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;
wxString localeId = CanonicalRef.empty() ? CanonicalName : CanonicalRef;
wxUILocale uiLocale = wxUILocale::FromTag(localeId);
wxString localeName = uiLocale.IsSupported() ? uiLocale.GetName() : wxString();
return localeName;
}
// ----------------------------------------------------------------------------
// wxUILocale / wxLocale
// wxUILocale
// ----------------------------------------------------------------------------
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 */
void wxUILocale::AddLanguage(const wxLanguageInfo& info)
{
@@ -451,95 +214,6 @@ void wxUILocale::AddLanguage(const wxLanguageInfo& 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
// ----------------------------------------------------------------------------
@@ -629,9 +303,25 @@ bool wxLocale::Init(const wxString& name,
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,
@@ -740,7 +430,7 @@ bool wxLocale::Init(int lang, int flags)
else
{
name = info->Description;
shortName = info->CanonicalName;
shortName = info->CanonicalRef.empty() ? info->CanonicalName : info->CanonicalRef;
}
DoInit(name, shortName, lang);
@@ -748,9 +438,14 @@ bool wxLocale::Init(int lang, int flags)
// Set the locale:
#if defined(__UNIX__) || defined(__WIN32__)
const wxString oldUILocale = wxUILocale::GetCurrent().GetName();
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
// the other platforms we still have to do it here.
@@ -1015,15 +710,17 @@ wxLocale::~wxLocale()
// restore old locale pointer
wxSetLocale(m_pOldLocale);
// and old current wxUILocale
if (!m_oldUILocale.empty())
{
wxUILocale::UseLocaleName(m_oldUILocale);
}
if ( m_pszOldLocale )
{
wxSetlocale(LC_ALL, 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;
}
#if defined(__WIN32__)
if ( !info->WinLang )
return false;
wxString localeTag = info->CanonicalRef.empty() ? info->LocaleTag : info->CanonicalRef;
wxUILocale uiLocale(wxLocaleIdent::FromTag(localeTag));
if ( !::IsValidLocale(info->GetLCID(), LCID_INSTALLED) )
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;
return uiLocale.IsSupported();
}
@@ -1578,38 +1234,6 @@ LCTYPE wxGetLCTYPEFormatFromLocalInfo(wxLocaleInfo index)
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.
wxString
wxGetInfoFromLCID(LCID lcid, wxLocaleInfo index, wxLocaleCategory cat)
@@ -1680,35 +1304,13 @@ wxGetInfoFromLCID(LCID lcid, wxLocaleInfo index, wxLocaleCategory cat)
/* static */
wxString wxLocale::GetInfo(wxLocaleInfo index, wxLocaleCategory cat)
{
if ( !wxGetLocale() )
{
// 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);
return wxUILocale::GetCurrent().GetInfo(index, cat);
}
/* static */
wxString wxLocale::GetOSInfo(wxLocaleInfo index, wxLocaleCategory cat)
{
return GetInfoFromLCID(::GetThreadLocale(), index, cat);
return wxUILocale::GetCurrent().GetInfo(index, cat);
}
#elif defined(__WXOSX__)

View File

@@ -23,6 +23,7 @@
#include "wx/uilocale.h"
#include "wx/arrstr.h"
#include "wx/intl.h"
#ifndef __WINDOWS__
#include "wx/language.h"
@@ -30,6 +31,35 @@
#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
// ----------------------------------------------------------------------------
@@ -50,27 +80,97 @@ wxUILocale wxUILocale::ms_current;
/* static */
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
// full syntax. Here we fully support just the subset we're interested in:
//
// - 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.
if ( tag != tag.ToAscii() )
// Locale tags must always use alphanumeric characters and certain special characters "-_.@"
if (tag.find_first_not_of(validCharsTag) != wxString::npos)
{
wxFAIL_MSG("tag contains invalid characters (not alphanumeric) or invalid delimiters");
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();
if ( it == parts.end() )
return wxLocaleIdent();
// We have at least the language, so we'll return a valid object.
wxLocaleIdent locId;
locId.m_language = *it;
locId.m_language = (*it).Lower();
// Also store the full string, so that the platforms that support BCP 47
// natively can use it instead of reconstructing the string from our fields.
// Also store the full string.
locId.m_tag = tag;
if ( ++it == parts.end() )
@@ -103,82 +203,209 @@ wxLocaleIdent wxLocaleIdent::FromTag(const wxString& tag)
case 2:
case 3:
// Either an ISO 3166-1 or UN M.49 region code.
locId.m_region = *it;
locId.m_region = (*it).Upper();
break;
case 4:
// 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;
default:
// This looks to be completely invalid.
wxFAIL_MSG("invalid code length, script or region code expected");
return wxLocaleIdent();
}
// If we got the language and the region, we can't parse anything else
// (variants, extensions, private use) anyhow.
if ( !locId.m_region.empty() )
return locId;
// Otherwise we must have got the script above, so check if we have the
// region too.
// Check whether we have got the region above.
// If not, we must have got the script. So, check if we have the region, too.
if (++it == parts.end())
{
CheckLanguageVariant(locId);
return locId;
}
if (locId.m_region.empty())
{
switch (it->length())
{
case 2:
case 3:
locId.m_region = *it;
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;
}
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;
}
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;
}
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;
}
wxLocaleIdent& wxLocaleIdent::Charset(const wxString& charset)
{
if (charset.find_first_not_of(validCharsModExt) == wxString::npos)
{
m_charset = charset;
}
else
{
m_charset.clear();
}
return *this;
}
wxLocaleIdent& wxLocaleIdent::Modifier(const wxString& modifier)
{
if (modifier.find_first_not_of(validCharsModExt) == wxString::npos)
{
m_modifier = modifier;
}
else
{
m_modifier.clear();
}
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;
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;
case wxLOCALE_TAGTYPE_MACOS:
if (!m_script.empty())
tag << '-' << m_script;
if (!m_region.empty())
tag << '_' << m_region;
break;
case wxLOCALE_TAGTYPE_POSIX:
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;
}
@@ -240,9 +467,30 @@ bool wxUILocale::UseDefault()
}
/* static */
bool wxUILocale::UseLanguage(const wxLanguageInfo& info)
bool wxUILocale::UseLocaleName(const wxString& localeName)
{
wxUILocaleImpl* const impl = wxUILocaleImpl::CreateForLanguage(info);
wxUILocaleImpl* impl = NULL;
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;
@@ -308,6 +556,14 @@ wxString wxUILocale::GetName() const
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
{
if ( !m_impl )
@@ -316,6 +572,35 @@ wxString wxUILocale::GetInfo(wxLocaleInfo index, wxLocaleCategory cat) const
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
wxUILocale::CompareStrings(const wxString& lhs,
const wxString& rhs,
@@ -341,4 +626,180 @@ wxUILocale::~wxUILocale()
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

View File

@@ -25,6 +25,7 @@
#include "wx/msw/private/uilocale.h"
#include "wx/scopedarray.h"
#include "wx/dynlib.h"
#ifndef LOCALE_NAME_USER_DEFAULT
@@ -35,6 +36,42 @@
#define MUI_LANGUAGE_NAME 8
#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
// ============================================================================
@@ -96,11 +133,105 @@ wxString wxLocaleIdent::GetName() const
{
name << "-" << m_region;
}
if (!m_extension.empty())
{
name << "-" << m_extension;
}
if (!m_sortorder.empty())
{
name << "_" << m_sortorder;
}
}
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
// ----------------------------------------------------------------------------
@@ -121,23 +252,27 @@ public:
wxString GetName() const wxOVERRIDE
{
wxChar buf[256];
buf[0] = wxT('\0');
wxString str;
// Try using newer constant available since Vista which produces names
// more similar to the other platforms.
if ( wxGetWinVersion() >= wxWinVersion_Vista )
{
::GetLocaleInfo(m_lcid, LOCALE_SNAME, buf, WXSIZEOF(buf));
str = DoGetInfo(LOCALE_SNAME);
}
else // TODO-XP: Drop this branch.
{
// This name constant is available under all systems, including
// 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
@@ -145,6 +280,68 @@ public:
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 flags) const wxOVERRIDE
{
@@ -158,6 +355,18 @@ public:
private:
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);
};
@@ -186,6 +395,9 @@ public:
wxDL_INIT_FUNC(ms_, SetThreadPreferredUILanguages, dllKernel32);
if ( !ms_SetThreadPreferredUILanguages )
return false;
wxDL_INIT_FUNC(ms_, GetUserPreferredUILanguages, dllKernel32);
if (!ms_GetUserPreferredUILanguages)
return false;
wxDL_INIT_FUNC(ms_, CompareStringEx, dllKernel32);
if ( !ms_CompareStringEx )
@@ -197,6 +409,66 @@ public:
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.
static wxUILocaleImplName* CreateDefault()
{
@@ -212,6 +484,37 @@ public:
if ( !ms_GetLocaleInfoEx(name, LOCALE_SNAME, NULL, 0) )
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);
}
@@ -242,6 +545,11 @@ public:
return DoGetInfo(LOCALE_SNAME);
}
wxLocaleIdent GetLocaleId() const wxOVERRIDE
{
return wxLocaleIdent::FromTag(GetName());
}
wxString GetInfo(wxLocaleInfo index, wxLocaleCategory cat) const wxOVERRIDE
{
// TODO-XP: This duplicates code from in wxGetInfoFromLCID(), but
@@ -286,6 +594,93 @@ public:
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 flags) const wxOVERRIDE
{
@@ -326,6 +721,9 @@ private:
typedef BOOL (WINAPI *SetThreadPreferredUILanguages_t)(DWORD, CONST WCHAR*, ULONG*);
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
// don't bother dealing with the different sizes of this struct under
// different OS versions and define the function type as using "void*" to
@@ -367,6 +765,7 @@ private:
wxUILocaleImplName::GetLocaleInfoEx_t wxUILocaleImplName::ms_GetLocaleInfoEx;
wxUILocaleImplName::SetThreadPreferredUILanguages_t wxUILocaleImplName::ms_SetThreadPreferredUILanguages;
wxUILocaleImplName::GetUserPreferredUILanguages_t wxUILocaleImplName::ms_GetUserPreferredUILanguages;
wxUILocaleImplName::CompareStringEx_t wxUILocaleImplName::ms_CompareStringEx;
// ----------------------------------------------------------------------------
@@ -376,14 +775,7 @@ wxUILocaleImplName::CompareStringEx_t wxUILocaleImplName::ms_CompareStringEx;
/* static */
wxUILocaleImpl* wxUILocaleImpl::CreateStdC()
{
if ( !wxUILocaleImplName::CanUse() )
{
// 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");
return wxUILocaleImplStdC::Create();
}
/* static */
@@ -397,16 +789,21 @@ wxUILocaleImpl* wxUILocaleImpl::CreateUserDefault()
/* static */
wxUILocaleImpl* wxUILocaleImpl::CreateForLanguage(const wxLanguageInfo& info)
{
if (!wxUILocaleImplName::CanUse())
{
if (info.WinLang == 0)
{
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());
}
}
/* static */
wxUILocaleImpl* wxUILocaleImpl::CreateForLocale(const wxLocaleIdent& locId)
@@ -420,7 +817,13 @@ wxUILocaleImpl* wxUILocaleImpl::CreateForLocale(const wxLocaleIdent& locId)
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

View File

@@ -101,7 +101,10 @@ public:
void Use() wxOVERRIDE;
wxString GetName() const wxOVERRIDE;
wxLocaleIdent GetLocaleId() 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 flags) const wxOVERRIDE;
@@ -130,12 +133,78 @@ wxUILocaleImplCF::GetName() const
return wxCFStringRef::AsString([m_nsloc localeIdentifier]);
}
wxLocaleIdent
wxUILocaleImplCF::GetLocaleId() const
{
return wxLocaleIdent::FromTag(GetName());
}
wxString
wxUILocaleImplCF::GetInfo(wxLocaleInfo index, wxLocaleCategory cat) const
{
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 */
wxUILocaleImpl* wxUILocaleImpl::CreateStdC()
{
@@ -154,6 +223,19 @@ wxUILocaleImpl* wxUILocaleImpl::CreateForLocale(const wxLocaleIdent& 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
wxUILocaleImplCF::CompareStrings(const wxString& lhs, const wxString& rhs,
int flags) const

View File

@@ -26,6 +26,7 @@
#include "wx/unix/private/uilocale.h"
#include "wx/intl.h"
#include "wx/utils.h"
#include <locale.h>
#ifdef HAVE_LANGINFO_H
@@ -35,6 +36,13 @@
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
// ----------------------------------------------------------------------------
@@ -53,7 +61,11 @@ public:
void Use() wxOVERRIDE;
wxString GetName() const wxOVERRIDE;
wxLocaleIdent GetLocaleId() 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 flags) const wxOVERRIDE;
@@ -64,6 +76,7 @@ private:
#endif // HAVE_LANGINFO_H
wxLocaleIdent m_locId;
wxString m_codeset;
#ifdef HAVE_LOCALE_T
// Only null for the default locale.
@@ -85,15 +98,39 @@ inline locale_t TryCreateLocale(const wxLocaleIdent& locId)
// modifying its wxLocaleIdent argument if it succeeds).
locale_t TryCreateLocaleWithUTF8(wxLocaleIdent& locId)
{
locale_t loc = TryCreateLocale(locId);
if ( !loc && locId.GetCharset().empty() )
locale_t loc = NULL;
#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);
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 )
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;
}
@@ -107,8 +144,6 @@ locale_t TryCreateMatchingLocale(wxLocaleIdent& locId)
// Try to find a variant of this locale available on this system: first
// 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.
if ( locId.GetRegion().empty() )
{
const wxString lang = locId.GetLanguage();
const wxLanguageInfos& infos = wxGetLanguageInfos();
@@ -116,7 +151,7 @@ locale_t TryCreateMatchingLocale(wxLocaleIdent& locId)
it != infos.end();
++it )
{
const wxString& fullname = it->CanonicalName;
const wxString& fullname = it->CanonicalRef.empty() ? it->CanonicalName : it->CanonicalRef;
if ( fullname.BeforeFirst('_') == lang )
{
// We never have encoding in our canonical names, but we
@@ -141,7 +176,6 @@ locale_t TryCreateMatchingLocale(wxLocaleIdent& locId)
}
}
}
}
return loc;
}
@@ -170,7 +204,9 @@ wxString wxLocaleIdent::GetName() const
if ( !m_charset.empty() )
name << "." << m_charset;
if ( !m_modifier.empty() )
if ( !m_script.empty() )
name << "@" << wxUILocale::GetScriptAliasFromName(m_script);
else if ( !m_modifier.empty() )
name << "@" << m_modifier;
}
@@ -189,22 +225,22 @@ static const char *wxSetlocaleTryUTF8(int c, const wxLocaleIdent& locId)
if ( locId.GetCharset().empty() )
{
wxLocaleIdent locIdUTF8(locId);
locIdUTF8.Charset(wxS(".UTF-8"));
locIdUTF8.Charset(wxS("UTF-8"));
l = wxSetlocale(c, locIdUTF8.GetName());
if ( !l )
{
locIdUTF8.Charset(wxS(".utf-8"));
locIdUTF8.Charset(wxS("utf-8"));
l = wxSetlocale(c, locIdUTF8.GetName());
}
if ( !l )
{
locIdUTF8.Charset(wxS(".UTF8"));
locIdUTF8.Charset(wxS("UTF8"));
l = wxSetlocale(c, locIdUTF8.GetName());
}
if ( !l )
{
locIdUTF8.Charset(wxS(".utf8"));
locIdUTF8.Charset(wxS("utf8"));
l = wxSetlocale(c, locIdUTF8.GetName());
}
}
@@ -245,6 +281,11 @@ wxUILocaleImplUnix::wxUILocaleImplUnix(wxLocaleIdent locId
, m_locale(loc)
#endif // HAVE_LOCALE_T
{
#ifdef HAVE_LANGINFO_H
m_codeset = GetLangInfo(CODESET);
#else
m_codeset = "";
#endif // HAVE_LANGINFO_H
}
wxUILocaleImplUnix::~wxUILocaleImplUnix()
@@ -292,7 +333,20 @@ wxUILocaleImplUnix::Use()
wxString
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
@@ -360,6 +414,86 @@ wxUILocaleImplUnix::GetInfo(wxLocaleInfo index, wxLocaleCategory cat) const
#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
wxUILocaleImplUnix::CompareStrings(const wxString& lhs, const wxString& rhs,
int WXUNUSED(flags)) const
@@ -413,4 +547,127 @@ wxUILocaleImpl* wxUILocaleImpl::CreateForLocale(const wxLocaleIdent& locIdOrig)
#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

View File

@@ -262,7 +262,8 @@ TEST_CASE("wxUILocale::IsSupported", "[uilocale]")
{
CheckSupported(wxUILocale::FromTag("en"), "English");
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]")