diff --git a/CHANGES b/CHANGES
index f2d26ac..c5853c7 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,14 @@
+Version 2.14.1
+==============
+
+Work in progress
+
+* Fixes:
+
+  - Fix thread safety of encoding and decoding when `uselocale` or `newlocale`
+    is used to switch locales inside the threads (#674, #675, #677. Thanks to
+    Bruno Haible the report and help with fixing.)
+
 Version 2.14
 ============
 
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8788afe..4a93a71 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -215,18 +215,7 @@ if (NOT DEFINED JSON_INT_T)
    endif ()
 endif ()
 
-
-# If locale.h and localeconv() are available, define to 1, otherwise to 0.
 check_include_files (locale.h HAVE_LOCALE_H)
-check_function_exists (localeconv HAVE_LOCALECONV)
-
-if (HAVE_LOCALECONV AND HAVE_LOCALE_H)
-   set (JSON_HAVE_LOCALECONV 1)
-else ()
-   set (JSON_HAVE_LOCALECONV 0)
-endif()
-
-# check if we have setlocale
 check_function_exists(setlocale HAVE_SETLOCALE)
 
 # Check what the inline keyword is.
diff --git a/android/jansson_config.h b/android/jansson_config.h
index 618a0da..448010a 100644
--- a/android/jansson_config.h
+++ b/android/jansson_config.h
@@ -32,10 +32,6 @@
    otherwise to 0. */
 #define JSON_INTEGER_IS_LONG_LONG 1
 
-/* If locale.h and localeconv() are available, define to 1,
-   otherwise to 0. */
-#define JSON_HAVE_LOCALECONV 0
-
 /* Maximum recursion depth for parsing JSON input.
    This limits the depth of e.g. array-within-array constructions. */
 #define JSON_PARSER_MAX_DEPTH 2048
diff --git a/cmake/jansson_config.h.cmake b/cmake/jansson_config.h.cmake
index 2f248cb..542e57b 100644
--- a/cmake/jansson_config.h.cmake
+++ b/cmake/jansson_config.h.cmake
@@ -56,9 +56,6 @@
 #define JSON_INTEGER_FORMAT @JSON_INTEGER_FORMAT@
 
 
-/* If locale.h and localeconv() are available, define to 1, otherwise to 0. */
-#define JSON_HAVE_LOCALECONV @JSON_HAVE_LOCALECONV@
-
 /* If __atomic builtins are available they will be used to manage
    reference counts of json_t. */
 #define JSON_HAVE_ATOMIC_BUILTINS @JSON_HAVE_ATOMIC_BUILTINS@
diff --git a/configure.ac b/configure.ac
index f022eb7..15721f5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -34,7 +34,7 @@ esac
 AC_SUBST([json_inline])
 
 # Checks for library functions.
-AC_CHECK_FUNCS([close getpid gettimeofday localeconv open read sched_yield strtoll])
+AC_CHECK_FUNCS([close getpid gettimeofday open read setlocale sched_yield strtoll])
 
 AC_MSG_CHECKING([for gcc __sync builtins])
 have_sync_builtins=no
@@ -74,12 +74,6 @@ case "$ac_cv_type_long_long_int$ac_cv_func_strtoll" in
 esac
 AC_SUBST([json_have_long_long])
 
-case "$ac_cv_header_locale_h$ac_cv_func_localeconv" in
-     yesyes) json_have_localeconv=1;;
-     *) json_have_localeconv=0;;
-esac
-AC_SUBST([json_have_localeconv])
-
 # Features
 AC_ARG_ENABLE([urandom],
   [AS_HELP_STRING([--disable-urandom],
diff --git a/src/jansson_config.h.in b/src/jansson_config.h.in
index fe692ab..791f60d 100644
--- a/src/jansson_config.h.in
+++ b/src/jansson_config.h.in
@@ -32,10 +32,6 @@
    otherwise to 0. */
 #define JSON_INTEGER_IS_LONG_LONG @json_have_long_long@
 
-/* If locale.h and localeconv() are available, define to 1,
-   otherwise to 0. */
-#define JSON_HAVE_LOCALECONV @json_have_localeconv@
-
 /* If __atomic builtins are available they will be used to manage
    reference counts of json_t. */
 #define JSON_HAVE_ATOMIC_BUILTINS @json_have_atomic_builtins@
diff --git a/src/strconv.c b/src/strconv.c
index c6f4fd1..7d317dd 100644
--- a/src/strconv.c
+++ b/src/strconv.c
@@ -11,57 +11,57 @@
 #include <jansson_private_config.h>
 #endif
 
-#if JSON_HAVE_LOCALECONV
-#include <locale.h>
-
 /*
   - This code assumes that the decimal separator is exactly one
     character.
 
   - If setlocale() is called by another thread between the call to
-    localeconv() and the call to sprintf() or strtod(), the result may
-    be wrong. setlocale() is not thread-safe and should not be used
-    this way. Multi-threaded programs should use uselocale() instead.
+    get_decimal_point() and the call to sprintf() or strtod(), the
+    result may be wrong. setlocale() is not thread-safe and should
+    not be used this way. Multi-threaded programs should use
+    uselocale() instead.
 */
+static char get_decimal_point() {
+    char buf[3];
+    sprintf(buf, "%#.0f", 1.0); // "1." in the current locale
+    return buf[1];
+}
 
 static void to_locale(strbuffer_t *strbuffer) {
-    const char *point;
+    char point;
     char *pos;
 
-    point = localeconv()->decimal_point;
-    if (*point == '.') {
+    point = get_decimal_point();
+    if (point == '.') {
         /* No conversion needed */
         return;
     }
 
     pos = strchr(strbuffer->value, '.');
     if (pos)
-        *pos = *point;
+        *pos = point;
 }
 
 static void from_locale(char *buffer) {
-    const char *point;
+    char point;
     char *pos;
 
-    point = localeconv()->decimal_point;
-    if (*point == '.') {
+    point = get_decimal_point();
+    if (point == '.') {
         /* No conversion needed */
         return;
     }
 
-    pos = strchr(buffer, *point);
+    pos = strchr(buffer, point);
     if (pos)
         *pos = '.';
 }
-#endif
 
 int jsonp_strtod(strbuffer_t *strbuffer, double *out) {
     double value;
     char *end;
 
-#if JSON_HAVE_LOCALECONV
     to_locale(strbuffer);
-#endif
 
     errno = 0;
     value = strtod(strbuffer->value, &end);
@@ -92,9 +92,7 @@ int jsonp_dtostr(char *buffer, size_t size, double value, int precision) {
     if (length >= size)
         return -1;
 
-#if JSON_HAVE_LOCALECONV
     from_locale(buffer);
-#endif
 
     /* Make sure there's a dot or 'e' in the output. Otherwise
        a real is converted to an integer when decoding */