summaryrefslogtreecommitdiff
path: root/libs/pbd/locale_guard.cc
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2016-07-14 11:35:55 -0400
committerPaul Davis <paul@linuxaudiosystems.com>2016-07-14 11:37:48 -0400
commit58657edfb3f3705366f88cd0cfec8e1d08cee97d (patch)
tree4b02edc9f11d9ee968e20d3f4bd48ee6efbfe37d /libs/pbd/locale_guard.cc
parent543e230f79379edba2d84211d18505536b71777f (diff)
redesign PBD::LocaleGuard to work correctly (i.e. not break translations)
Diffstat (limited to 'libs/pbd/locale_guard.cc')
-rw-r--r--libs/pbd/locale_guard.cc90
1 files changed, 54 insertions, 36 deletions
diff --git a/libs/pbd/locale_guard.cc b/libs/pbd/locale_guard.cc
index b4225d3875..f5f97f2042 100644
--- a/libs/pbd/locale_guard.cc
+++ b/libs/pbd/locale_guard.cc
@@ -29,54 +29,72 @@
using namespace PBD;
-/* The initial C++ locale is "C" regardless of the user's preferred locale.
- * The C locale from setlocale() matches the user's preferred locale
+/* Neither C nor C++ pick up a user's preferred locale choice without the
+ * application actively taking steps to make this happen.
+ *
+ * For C: setlocale (LC_ALL, "");
+ * For C++ (assuming that the C version was called):
+ * std::locale::global (std::locale (setlocale (LC_ALL, 0)));
+ *
+ * The application needs to make these calls, probably in main().
*
* Setting the C++ locale will change the C locale, but not the other way 'round.
* and some plugin may change either behind our back.
*/
LocaleGuard::LocaleGuard ()
- : old_c (0)
+ : old_c_locale (0)
{
- char* actual = setlocale (LC_NUMERIC, NULL);
- if (strcmp ("C", actual)) {
- DEBUG_TRACE (DEBUG::Locale, string_compose ("LG: change locale from '%1' to 'C'\n", actual));
- /* purpose of LocaleGuard is to make sure we're using "C" for
- the numeric locale during its lifetime, so make it so.
- */
- old_c = strdup (actual);
- /* this changes both C++ and C locale */
- std::locale::global (std::locale (std::locale::classic(), "C", std::locale::numeric));
- }
- if (old_cpp != std::locale::classic ()) {
- PBD::error << "LocaleGuard: initial C++ locale is not 'C'. Expect non-portable session files.\n";
- DEBUG_TRACE (DEBUG::Locale, "LG: initial C++ locale is not 'C'. Expect non-portable session files.\n");
+ /* A LocaleGuard object ensures that the
+ * LC_NUMERIC/std::locale::numeric aspect of the C and C++ locales are
+ * set to "C" during its lifetime, so that printf/iostreams use a
+ * portable format for numeric output (i.e. 1234.5 is always 1234.5 and
+ * not sometimes 1234,5, as it would be in fr or de locales)
+ */
+
+ char const * const current_c_locale = setlocale (LC_NUMERIC, 0);
+
+ if (strcmp ("C", current_c_locale) != 0) {
+
+ old_c_locale = strdup (current_c_locale);
+
+ /* set the C++ global/default locale to whatever we are using
+ * now, but with "C" numeric handling.
+ *
+ * this also sets the C locale, so no call to setlocale() is required.
+ */
+
+ std::locale::global (std::locale (old_cpp_locale, "C", std::locale::numeric));
+ pre_cpp_locale = std::locale();
+
+ DEBUG_TRACE (DEBUG::Locale, string_compose ("LG: change locale from '%1' => %2\n", old_cpp_locale.name(), pre_cpp_locale.name()));
}
}
LocaleGuard::~LocaleGuard ()
{
- char* actual = setlocale (LC_NUMERIC, NULL);
- std::locale current;
+ char const * current_c_locale = setlocale (LC_NUMERIC, 0);
+ std::locale current_cpp_locale;
- if (current != old_cpp) {
- /* the C++ locale should always be "C", that's the default
- * at application start, and ardour never changes it to
- * anything but "C".
- *
- * if it's not: some plugin meddled with it.
- */
- if (old_cpp != std::locale::classic ()) {
- PBD::error << "LocaleGuard: someone (a plugin) changed the C++ locale, expect non-portable session files.\n";
- DEBUG_TRACE (DEBUG::Locale, "LG: someone (a plugin) changed the C++ locale, expect non-portable session files.\n");
- }
- std::locale::global (old_cpp);
- DEBUG_TRACE (DEBUG::Locale, string_compose ("LG: restore C++ locale: '%1'\n", old_cpp.name ()));
- }
- if (old_c && strcmp (old_c, actual)) {
- setlocale (LC_NUMERIC, old_c);
- DEBUG_TRACE (DEBUG::Locale, string_compose ("LG: restore C locale: '%1'\n", old_c));
+ if (current_cpp_locale != pre_cpp_locale) {
+
+ PBD::error << string_compose ("LocaleGuard: someone (a plugin) changed the C++ locale from\n\t%1\nto\n\t%2\n, expect non-portable session files. Decimal OK ? %2",
+ old_cpp_locale.name(), current_cpp_locale.name(),
+ (std::use_facet<std::numpunct<char> >(std::locale()).decimal_point() == '.'))
+ << endmsg;
+ DEBUG_TRACE (DEBUG::Locale, string_compose ("LG: someone (a plugin) changed the C++ locale (Decimal OK ? '%1'); expect non-portable session files.\n",
+ (std::use_facet<std::numpunct<char> >(std::locale()).decimal_point() == '.')));
+
+ /* this resets C & C++ locales */
+ std::locale::global (old_cpp_locale);
+ DEBUG_TRACE (DEBUG::Locale, string_compose ("LG: restore C & C++ locale: '%1'\n", std::locale().name()));
+
+ } else if (old_c_locale && (strcmp (current_c_locale, old_c_locale) != 0)) {
+
+ /* reset only the C locale */
+ setlocale (LC_NUMERIC, old_c_locale);
+ DEBUG_TRACE (DEBUG::Locale, string_compose ("LG: restore C locale from %1 to\n'%2'\n(C++ is '%3')\n", current_c_locale, old_c_locale, std::locale().name()));
}
- free (old_c);
+
+ free (const_cast<char*> (old_c_locale));
}