v1-0001-NLS-use-gettext-to-translate-system-error-message.patch
text/x-patch
Filename: v1-0001-NLS-use-gettext-to-translate-system-error-message.patch
Type: text/x-patch
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v1-0001
Subject: NLS: use gettext() to translate system error messages.
| File | + | − |
|---|---|---|
| src/port/snprintf.c | 71 | 2 |
From 073bd5416b4c31bbe08975a6863ae8a80d0542da Mon Sep 17 00:00:00 2001
From: Jeff Davis <jeff@j-davis.com>
Date: Wed, 22 Oct 2025 10:49:59 -0700
Subject: [PATCH v1] NLS: use gettext() to translate system error messages.
Previously, errors from the system such as "Permission denied"
(EACCES) relied on strerror_r() to perform translation; which has
different behavior from gettext(), which translates Postgres error
messages like "division by zero".
Disable translations inside of strerror_r() by temporarily switching
to the C locale, and instead perform the translations with gettext.
This makes translation of system error messages consistent across
platforms, respecting whether NLS is enabled or not. It also avoids
strerror_r()'s dependence on the global LC_CTYPE setting (gettext does
not rely on LC_CTYPE).
Creates a need to translate more messages -- one for each errno that
Postgres might plausibly encounter, or possibly a few more for
platform variations of the string representations.
---
src/port/snprintf.c | 73 +++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 71 insertions(+), 2 deletions(-)
diff --git a/src/port/snprintf.c b/src/port/snprintf.c
index d7f18b42d19..ace39f2673e 100644
--- a/src/port/snprintf.c
+++ b/src/port/snprintf.c
@@ -33,6 +33,9 @@
#include "c.h"
+#ifdef HAVE_USELOCALE
+#include <locale.h>
+#endif
#include <math.h>
/*
@@ -161,6 +164,8 @@ typedef union
static void flushbuffer(PrintfTarget *target);
static void dopr(PrintfTarget *target, const char *format, va_list args);
+static char *c_strerror_r(int errnum, char *buf, size_t buflen);
+static char *nls_strerror_r(int errnum, char *buf, size_t buflen);
/*
@@ -724,8 +729,8 @@ nextch2:
case 'm':
{
char errbuf[PG_STRERROR_R_BUFLEN];
- const char *errm = strerror_r(save_errno,
- errbuf, sizeof(errbuf));
+ const char *errm = nls_strerror_r(save_errno,
+ errbuf, sizeof(errbuf));
dostr(errm, strlen(errm), target);
}
@@ -1566,3 +1571,67 @@ trailing_pad(int padlen, PrintfTarget *target)
if (padlen < 0)
dopr_outchmulti(' ', -padlen, target);
}
+
+
+/*
+ * If NLS is enabled, translate the system error message. Otherwise, return
+ * the untranslated string.
+ */
+static char *
+nls_strerror_r(int errnum, char *buf, size_t buflen)
+{
+#ifdef ENABLE_NLS
+ char plain[PG_STRERROR_R_BUFLEN];
+ char *msgid;
+ char *msgstr;
+
+ /* run c_strerror_r to get plain untranslated string */
+ msgid = c_strerror_r(errnum, plain, PG_STRERROR_R_BUFLEN);
+
+ /* translate with gettext() and store in result buffer */
+ msgstr = _(msgid);
+ strlcpy(buf, msgstr, buflen);
+ return buf;
+#else
+ return c_strerror_r(errnum, buf, buflen);
+#endif
+}
+
+/*
+ * Temporarily switches to the C locale to ensure that strerror_r() returns an
+ * untranslated string.
+ *
+ * The purpose of this function is to avoid strerror_r() performing the
+ * translation itself, which has different behavior than gettext. In
+ * particular, strerror_r() may force the translated message into the ASCII
+ * character set if LC_CTYPE=C, even if the database encoding supports a wider
+ * character set (e.g. UTF-8). We also want to avoid translations when NLS is
+ * disabled.
+ */
+static char *
+c_strerror_r(int errnum, char *buf, size_t buflen)
+{
+#ifdef HAVE_USELOCALE
+ static locale_t c_locale = NULL;
+ char *msgid;
+ locale_t save_loc;
+
+ if (!c_locale)
+ c_locale = newlocale(LC_ALL_MASK, "C", NULL);
+
+ save_loc = uselocale(c_locale);
+
+ msgid = strerror_r(errnum, buf, buflen);
+
+ if (save_loc != NULL)
+ uselocale(save_loc);
+
+ return msgid;
+#else
+ /*
+ * Platforms lacking uselocale() have not been observed to translate
+ * messages inside strerror_r().
+ */
+ return strerror_r(errnum, buf, buflen);
+#endif
+}
--
2.43.0