pg.ddx.io  pgsql-hackers@postgresql.org mailing list archive  
help / color / mirror / Atom feed
From: Tom Lane <tgl@sss.pgh.pa.us>
To: pgsql-hackers@lists.postgresql.org
Subject: Let's add a test for NLS translation of PRI* macros
Date: Mon, 08 Dec 2025 14:23:16 -0500
Message-ID: <3098752.1765221796@sss.pgh.pa.us> (raw)

This was discussed a few weeks ago in [1], but I'm starting a
new thread so as not to confuse things with the latest patches in
that thread.  The issue is that it seems like it'd be a good idea to
have specific regression testing of whether translation of the
<inttypes.h> PRI* macros works properly, since that is an aspect
of gettext() behavior that didn't use to work everywhere.

I don't know whether the attached will pass on Windows: we might
not be able to assume that "es_ES" is the right LC_MESSAGES
setting to use.  But it works for me on Linux.

			regards, tom lane

[1] https://www.postgresql.org/message-id/flat/20250331.152829.1921392690375275165.horikyota.ntt%40gmail...

Attachments:

v3-0001-Add-a-regression-test-to-verify-that-NLS-translat.patch (text/x-diff, 12.3K) download From d65fbf666e93fc3dd8c0e7c9817ace3c014ab99d Mon Sep 17 00:00:00 2001 From: Tom Lane <tgl@sss.pgh.pa.us> Date: Mon, 8 Dec 2025 14:14:59 -0500 Subject: [PATCH v3] Add a regression test to verify that NLS translation works. We've never actually had a formal test for this facility. It seems worth adding one now, mainly because we are starting to depend on gettext() being able to handle the PRI* macros from <inttypes.h>, and it's not all that certain that that works everywhere. So the test goes to a bit of effort to check all the PRI* macros we are likely to use. --- src/test/regress/expected/nls.out | 32 ++++++ src/test/regress/expected/nls_1.out | 17 +++ src/test/regress/meson.build | 2 + src/test/regress/nls.mk | 5 + src/test/regress/parallel_schedule | 2 +- src/test/regress/po/LINGUAS | 1 + src/test/regress/po/es.po | 159 ++++++++++++++++++++++++++++ src/test/regress/po/meson.build | 3 + src/test/regress/regress.c | 63 +++++++++++ src/test/regress/sql/nls.sql | 16 +++ 10 files changed, 299 insertions(+), 1 deletion(-) create mode 100644 src/test/regress/expected/nls.out create mode 100644 src/test/regress/expected/nls_1.out create mode 100644 src/test/regress/nls.mk create mode 100644 src/test/regress/po/LINGUAS create mode 100644 src/test/regress/po/es.po create mode 100644 src/test/regress/po/meson.build create mode 100644 src/test/regress/sql/nls.sql diff --git a/src/test/regress/expected/nls.out b/src/test/regress/expected/nls.out new file mode 100644 index 00000000000..d1afc528a8d --- /dev/null +++ b/src/test/regress/expected/nls.out @@ -0,0 +1,32 @@ +-- directory paths and dlsuffix are passed to us in environment variables +\getenv libdir PG_LIBDIR +\getenv dlsuffix PG_DLSUFFIX +\set regresslib :libdir '/regress' :dlsuffix +CREATE FUNCTION test_translation() + RETURNS void + AS :'regresslib' + LANGUAGE C; +SET lc_messages = 'es_ES'; +SELECT test_translation(); +NOTICE: traducido PRId64 = 424242424242 +NOTICE: traducido PRId32 = -1234 +NOTICE: traducido PRIdMAX = -5678 +NOTICE: traducido PRIdPTR = 9999 +NOTICE: traducido PRIu64 = 424242424242 +NOTICE: traducido PRIu32 = 1234 +NOTICE: traducido PRIuMAX = 5678 +NOTICE: traducido PRIuPTR = 9999 +NOTICE: traducido PRIx64 = 62c6d1a9b2 +NOTICE: traducido PRIx32 = 4d2 +NOTICE: traducido PRIxMAX = 162e +NOTICE: traducido PRIxPTR = 270f +NOTICE: traducido PRIX64 = 62C6D1A9B2 +NOTICE: traducido PRIX32 = 4D2 +NOTICE: traducido PRIXMAX = 162E +NOTICE: traducido PRIXPTR = 270F + test_translation +------------------ + +(1 row) + +RESET lc_messages; diff --git a/src/test/regress/expected/nls_1.out b/src/test/regress/expected/nls_1.out new file mode 100644 index 00000000000..4b707e9dad4 --- /dev/null +++ b/src/test/regress/expected/nls_1.out @@ -0,0 +1,17 @@ +-- directory paths and dlsuffix are passed to us in environment variables +\getenv libdir PG_LIBDIR +\getenv dlsuffix PG_DLSUFFIX +\set regresslib :libdir '/regress' :dlsuffix +CREATE FUNCTION test_translation() + RETURNS void + AS :'regresslib' + LANGUAGE C; +SET lc_messages = 'es_ES'; +SELECT test_translation(); +NOTICE: NLS is not enabled + test_translation +------------------ + +(1 row) + +RESET lc_messages; diff --git a/src/test/regress/meson.build b/src/test/regress/meson.build index 1da9e9462a9..4001a81ffe5 100644 --- a/src/test/regress/meson.build +++ b/src/test/regress/meson.build @@ -57,3 +57,5 @@ tests += { 'dbname': 'regression', }, } + +subdir('po', if_found: libintl) diff --git a/src/test/regress/nls.mk b/src/test/regress/nls.mk new file mode 100644 index 00000000000..43227c64f09 --- /dev/null +++ b/src/test/regress/nls.mk @@ -0,0 +1,5 @@ +# src/test/regress/nls.mk +CATALOG_NAME = regress +GETTEXT_FILES = regress.c +GETTEXT_TRIGGERS = $(BACKEND_COMMON_GETTEXT_TRIGGERS) +GETTEXT_FLAGS = $(BACKEND_COMMON_GETTEXT_FLAGS) diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index cc6d799bcea..0931f1dcccf 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -76,7 +76,7 @@ test: brin_bloom brin_multi # ---------- # Another group of parallel tests # ---------- -test: create_table_like alter_generic alter_operator misc async dbsize merge misc_functions sysviews tsrf tid tidscan tidrangescan collate.utf8 collate.icu.utf8 incremental_sort create_role without_overlaps generated_virtual +test: create_table_like alter_generic alter_operator misc async dbsize merge misc_functions nls sysviews tsrf tid tidscan tidrangescan collate.utf8 collate.icu.utf8 incremental_sort create_role without_overlaps generated_virtual # collate.linux.utf8 and collate.icu.utf8 tests cannot be run in parallel with each other # psql depends on create_am diff --git a/src/test/regress/po/LINGUAS b/src/test/regress/po/LINGUAS new file mode 100644 index 00000000000..8357fcaaed4 --- /dev/null +++ b/src/test/regress/po/LINGUAS @@ -0,0 +1 @@ +es diff --git a/src/test/regress/po/es.po b/src/test/regress/po/es.po new file mode 100644 index 00000000000..b3021d57e22 --- /dev/null +++ b/src/test/regress/po/es.po @@ -0,0 +1,159 @@ +# Spanish message translation file for regress test library +# +# Copyright (C) 2025 PostgreSQL Global Development Group +# This file is distributed under the same license as the regress (PostgreSQL) package. +# +# Tom Lane <tgl@sss.pgh.pa.us>, 2025. +# +msgid "" +msgstr "" +"Project-Id-Version: regress (PostgreSQL) 19\n" +"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n" +"POT-Creation-Date: 2025-12-08 13:57-0500\n" +"PO-Revision-Date: 2025-11-19 19:01-0500\n" +"Last-Translator: Tom Lane <tgl@sss.pgh.pa.us>\n" +"Language-Team: PgSQL-es-Ayuda <pgsql-es-ayuda@lists.postgresql.org>\n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: regress.c:202 +#, c-format +msgid "invalid input syntax for type %s: \"%s\"" +msgstr "la sintaxis de entrada no es válida para tipo %s: «%s»" + +#: regress.c:839 +#, c-format +msgid "test_inline_in_from_support_func called with %d args but expected 3" +msgstr "" + +#: regress.c:847 regress.c:863 +#, c-format +msgid "test_inline_in_from_support_func called with non-Const parameters" +msgstr "" + +#: regress.c:854 regress.c:870 +#, c-format +msgid "test_inline_in_from_support_func called with non-TEXT parameters" +msgstr "" + +#: regress.c:903 +#, c-format +msgid "test_inline_in_from_support_func parsed to more than one node" +msgstr "" + +#: regress.c:914 +#, c-format +msgid "test_inline_in_from_support_func rewrote to more than one node" +msgstr "" + +#: regress.c:921 +#, c-format +msgid "test_inline_in_from_support_func didn't parse to a Query" +msgstr "" + +#: regress.c:1028 +#, c-format +msgid "invalid source encoding name \"%s\"" +msgstr "la codificación de origen «%s» no es válida" + +#: regress.c:1033 +#, c-format +msgid "invalid destination encoding name \"%s\"" +msgstr "la codificación de destino «%s» no es válida" + +#: regress.c:1078 +#, c-format +msgid "default conversion function for encoding \"%s\" to \"%s\" does not exist" +msgstr "no existe el procedimiento por omisión de conversión desde la codificación «%s» a «%s»" + +#: regress.c:1085 +#, c-format +msgid "out of memory" +msgstr "memoria agotada" + +#: regress.c:1086 +#, c-format +msgid "String of %d bytes is too long for encoding conversion." +msgstr "La cadena de %d bytes es demasiado larga para la recodificación." + +#: regress.c:1175 +#, c-format +msgid "translated PRId64 = %<PRId64>" +msgstr "traducido PRId64 = %<PRId64>" + +#: regress.c:1177 +#, c-format +msgid "translated PRId32 = %<PRId32>" +msgstr "traducido PRId32 = %<PRId32>" + +#: regress.c:1179 +#, c-format +msgid "translated PRIdMAX = %<PRIdMAX>" +msgstr "traducido PRIdMAX = %<PRIdMAX>" + +#: regress.c:1181 +#, c-format +msgid "translated PRIdPTR = %<PRIdPTR>" +msgstr "traducido PRIdPTR = %<PRIdPTR>" + +#: regress.c:1184 +#, c-format +msgid "translated PRIu64 = %<PRIu64>" +msgstr "traducido PRIu64 = %<PRIu64>" + +#: regress.c:1186 +#, c-format +msgid "translated PRIu32 = %<PRIu32>" +msgstr "traducido PRIu32 = %<PRIu32>" + +#: regress.c:1188 +#, c-format +msgid "translated PRIuMAX = %<PRIuMAX>" +msgstr "traducido PRIuMAX = %<PRIuMAX>" + +#: regress.c:1190 +#, c-format +msgid "translated PRIuPTR = %<PRIuPTR>" +msgstr "traducido PRIuPTR = %<PRIuPTR>" + +#: regress.c:1193 +#, c-format +msgid "translated PRIx64 = %<PRIx64>" +msgstr "traducido PRIx64 = %<PRIx64>" + +#: regress.c:1195 +#, c-format +msgid "translated PRIx32 = %<PRIx32>" +msgstr "traducido PRIx32 = %<PRIx32>" + +#: regress.c:1197 +#, c-format +msgid "translated PRIxMAX = %<PRIxMAX>" +msgstr "traducido PRIxMAX = %<PRIxMAX>" + +#: regress.c:1199 +#, c-format +msgid "translated PRIxPTR = %<PRIxPTR>" +msgstr "traducido PRIxPTR = %<PRIxPTR>" + +#: regress.c:1202 +#, c-format +msgid "translated PRIX64 = %<PRIX64>" +msgstr "traducido PRIX64 = %<PRIX64>" + +#: regress.c:1204 +#, c-format +msgid "translated PRIX32 = %<PRIX32>" +msgstr "traducido PRIX32 = %<PRIX32>" + +#: regress.c:1206 +#, c-format +msgid "translated PRIXMAX = %<PRIXMAX>" +msgstr "traducido PRIXMAX = %<PRIXMAX>" + +#: regress.c:1208 +#, c-format +msgid "translated PRIXPTR = %<PRIXPTR>" +msgstr "traducido PRIXPTR = %<PRIXPTR>" diff --git a/src/test/regress/po/meson.build b/src/test/regress/po/meson.build new file mode 100644 index 00000000000..e9bd964aa7f --- /dev/null +++ b/src/test/regress/po/meson.build @@ -0,0 +1,3 @@ +# Copyright (c) 2022-2025, PostgreSQL Global Development Group + +nls_targets += [i18n.gettext('regress-' + pg_version_major.to_string())] diff --git a/src/test/regress/regress.c b/src/test/regress/regress.c index 56cc0567b1c..2d5d799f6e4 100644 --- a/src/test/regress/regress.c +++ b/src/test/regress/regress.c @@ -48,6 +48,10 @@ #include "utils/rel.h" #include "utils/typcache.h" +/* define our text domain for translations */ +#undef TEXTDOMAIN +#define TEXTDOMAIN PG_TEXTDOMAIN("regress") + #define EXPECT_TRUE(expr) \ do { \ if (!(expr)) \ @@ -1149,3 +1153,62 @@ test_relpath(PG_FUNCTION_ARGS) PG_RETURN_VOID(); } + +/* + * Simple test to verify NLS support, particularly that the PRI* macros work. + */ +PG_FUNCTION_INFO_V1(test_translation); +Datum +test_translation(PG_FUNCTION_ARGS) +{ +#ifdef ENABLE_NLS + /* This would be better done in _PG_init(), if this module had one */ + static bool inited = false; + + if (!inited) + { + pg_bindtextdomain(TEXTDOMAIN); + inited = true; + } + + ereport(NOTICE, + errmsg("translated PRId64 = %" PRId64, (int64) 424242424242)); + ereport(NOTICE, + errmsg("translated PRId32 = %" PRId32, (int32) -1234)); + ereport(NOTICE, + errmsg("translated PRIdMAX = %" PRIdMAX, (intmax_t) -5678)); + ereport(NOTICE, + errmsg("translated PRIdPTR = %" PRIdPTR, (intptr_t) 9999)); + + ereport(NOTICE, + errmsg("translated PRIu64 = %" PRIu64, (uint64) 424242424242)); + ereport(NOTICE, + errmsg("translated PRIu32 = %" PRIu32, (uint32) 1234)); + ereport(NOTICE, + errmsg("translated PRIuMAX = %" PRIuMAX, (uintmax_t) 5678)); + ereport(NOTICE, + errmsg("translated PRIuPTR = %" PRIuPTR, (uintptr_t) 9999)); + + ereport(NOTICE, + errmsg("translated PRIx64 = %" PRIx64, (uint64) 424242424242)); + ereport(NOTICE, + errmsg("translated PRIx32 = %" PRIx32, (uint32) 1234)); + ereport(NOTICE, + errmsg("translated PRIxMAX = %" PRIxMAX, (uintmax_t) 5678)); + ereport(NOTICE, + errmsg("translated PRIxPTR = %" PRIxPTR, (uintptr_t) 9999)); + + ereport(NOTICE, + errmsg("translated PRIX64 = %" PRIX64, (uint64) 424242424242)); + ereport(NOTICE, + errmsg("translated PRIX32 = %" PRIX32, (uint32) 1234)); + ereport(NOTICE, + errmsg("translated PRIXMAX = %" PRIXMAX, (uintmax_t) 5678)); + ereport(NOTICE, + errmsg("translated PRIXPTR = %" PRIXPTR, (uintptr_t) 9999)); +#else + elog(NOTICE, "NLS is not enabled"); +#endif + + PG_RETURN_VOID(); +} diff --git a/src/test/regress/sql/nls.sql b/src/test/regress/sql/nls.sql new file mode 100644 index 00000000000..53b4add86eb --- /dev/null +++ b/src/test/regress/sql/nls.sql @@ -0,0 +1,16 @@ +-- directory paths and dlsuffix are passed to us in environment variables +\getenv libdir PG_LIBDIR +\getenv dlsuffix PG_DLSUFFIX + +\set regresslib :libdir '/regress' :dlsuffix + +CREATE FUNCTION test_translation() + RETURNS void + AS :'regresslib' + LANGUAGE C; + +SET lc_messages = 'es_ES'; + +SELECT test_translation(); + +RESET lc_messages; -- 2.43.7

view thread (6+ messages)  latest in thread

Message-ID: <3098752.1765221796@sss.pgh.pa.us>
Permalink:  ../3098752.1765221796@sss.pgh.pa.us/
Also on:    postgresql.org/message-id/3098752.1765221796@sss.pgh.pa.us

 ·  · 

reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: pgsql-hackers@postgresql.org
  Cc: tgl@sss.pgh.pa.us, pgsql-hackers@lists.postgresql.org
  Subject: Re: Let's add a test for NLS translation of PRI* macros
  In-Reply-To: <3098752.1765221796@sss.pgh.pa.us>

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox