Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vU98x-000SkZ-06 for pgsql-hackers@arkaria.postgresql.org; Fri, 12 Dec 2025 19:49:36 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1vU98u-009EZO-2n for pgsql-hackers@arkaria.postgresql.org; Fri, 12 Dec 2025 19:49:33 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vU98u-009EZF-11 for pgsql-hackers@lists.postgresql.org; Fri, 12 Dec 2025 19:49:33 +0000 Received: from sss.pgh.pa.us ([68.162.161.243]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vU98s-000MXe-2r for pgsql-hackers@lists.postgresql.org; Fri, 12 Dec 2025 19:49:32 +0000 Received: from sss1.sss.pgh.pa.us (localhost [127.0.0.1]) by sss.pgh.pa.us (8.15.2/8.15.2) with ESMTP id 5BCJnRqF1038675; Fri, 12 Dec 2025 14:49:27 -0500 From: Tom Lane To: Peter Eisentraut cc: pgsql-hackers@lists.postgresql.org Subject: Re: Solaris versus our NLS files In-reply-to: <842387.1765473866@sss.pgh.pa.us> References: <292844.1765315339@sss.pgh.pa.us> <638279.1765383282@sss.pgh.pa.us> <9e77ce39-34e5-47c5-8f62-aeb6669df4ff@eisentraut.org> <842387.1765473866@sss.pgh.pa.us> Comments: In-reply-to Tom Lane message dated "Thu, 11 Dec 2025 12:24:26 -0500" MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="----- =_aaaaaaaaaa0" Content-ID: <1038643.1765568961.0@sss.pgh.pa.us> Date: Fri, 12 Dec 2025 14:49:27 -0500 Message-ID: <1038674.1765568967@sss.pgh.pa.us> List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk ------- =_aaaaaaaaaa0 Content-Type: text/plain; charset="us-ascii" Content-ID: <1038643.1765568961.1@sss.pgh.pa.us> I wrote: > Peter Eisentraut writes: >> I wonder how other gettext-using projects handle this on Solaris. > I suspect that the answer for most non-Solaris-specific projects has > been "use GNU gettext". I poked into that and it seems a lot messier than I hoped. At least on OpenIndiana, what's actually installed by the "GNU gettext" package is just the GNU flavors of the gettext command line tools, not a replacement libintl. There is a /lib/libintl.so.1, but forcing our code to link to that doesn't change the problematic behavior. So I feel that asking users to install GNU gettext is not going to be a practical solution. I notice in Oracle's docs [1] that creating symlinks in the install tree is just one way to implement a mapping from locale names to message catalogs: you can also do it at runtime by setting an environment variable. So what I'm now theorizing is that users have just learned to set that variable, which solves the problem across all packages despite the lack of symlinks. Hence, what I now propose to get my NLS-testing patch to work on Solaris is for the test to forcibly set that environment variable before invoking bindtextdomain: /* * Solaris' built-in gettext is not bright about associating locales * with message catalogs that are named after just the language. * Apparently the customary workaround is for users to set the * LANGUAGE environment variable to provide a mapping. Do so here to * ensure that the nls.sql regression test will work. */ #if defined(__sun__) setenv("LANGUAGE", "es_ES.UTF-8:es", 1); #endif pg_bindtextdomain(TEXTDOMAIN); This is surely a hack, but it's nicely localized and can be readily undone if anyone comes up with a better answer. I've verified that the attached v7 passes on current OpenIndiana. regards, tom lane [1] https://docs.oracle.com/cd/E36784_01/html/E39536/gnkbn.html ------- =_aaaaaaaaaa0 Content-Type: text/x-diff; name*0="v7-0001-Add-a-regression-test-to-verify-that-NLS-translat.p"; name*1="atch"; charset="UTF-8" Content-ID: <1038643.1765568961.2@sss.pgh.pa.us> Content-Description: v7-0001-Add-a-regression-test-to-verify-that-NLS-translat.patch Content-Transfer-Encoding: quoted-printable =46rom 0116f29fc2bb8c3b974e8d605b7523f448cc1d4d Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Fri, 12 Dec 2025 14:13:11 -0500 Subject: [PATCH v7] 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 , 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. (This effort has indeed found one problem already, now fixed in commit f8715ec86.) Discussion: https://postgr.es/m/3098752.1765221796@sss.pgh.pa.us --- src/test/regress/expected/nls.out | 35 ++++++ src/test/regress/expected/nls_1.out | 20 ++++ 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 | 77 ++++++++++++++ src/test/regress/sql/nls.sql | 19 ++++ 10 files changed, 322 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..5a650294eaf --- /dev/null +++ b/src/test/regress/expected/nls.out @@ -0,0 +1,35 @@ +-- 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; +-- Some BSDen are sticky about wanting a codeset name in lc_messages, +-- but it seems that at least on common platforms it doesn't have +-- to match the actual database encoding. +SET lc_messages =3D 'es_ES.UTF-8'; +SELECT test_translation(); +NOTICE: traducido PRId64 =3D 424242424242 +NOTICE: traducido PRId32 =3D -1234 +NOTICE: traducido PRIdMAX =3D -5678 +NOTICE: traducido PRIdPTR =3D 9999 +NOTICE: traducido PRIu64 =3D 424242424242 +NOTICE: traducido PRIu32 =3D 1234 +NOTICE: traducido PRIuMAX =3D 5678 +NOTICE: traducido PRIuPTR =3D 9999 +NOTICE: traducido PRIx64 =3D 62c6d1a9b2 +NOTICE: traducido PRIx32 =3D 4d2 +NOTICE: traducido PRIxMAX =3D 162e +NOTICE: traducido PRIxPTR =3D 270f +NOTICE: traducido PRIX64 =3D 62C6D1A9B2 +NOTICE: traducido PRIX32 =3D 4D2 +NOTICE: traducido PRIXMAX =3D 162E +NOTICE: traducido PRIXPTR =3D 270F + test_translation = +------------------ + = +(1 row) + +RESET lc_messages; diff --git a/src/test/regress/expected/nls_1.out b/src/test/regress/expect= ed/nls_1.out new file mode 100644 index 00000000000..9f1a2776e50 --- /dev/null +++ b/src/test/regress/expected/nls_1.out @@ -0,0 +1,20 @@ +-- 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; +-- Some BSDen are sticky about wanting a codeset name in lc_messages, +-- but it seems that at least on common platforms it doesn't have +-- to match the actual database encoding. +SET lc_messages =3D 'es_ES.UTF-8'; +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 +=3D { '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 =3D regress +GETTEXT_FILES =3D regress.c +GETTEXT_TRIGGERS =3D $(BACKEND_COMMON_GETTEXT_TRIGGERS) +GETTEXT_FLAGS =3D $(BACKEND_COMMON_GETTEXT_FLAGS) diff --git a/src/test/regress/parallel_schedule b/src/test/regress/paralle= l_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 me= rge misc_functions sysviews tsrf tid tidscan tidrangescan collate.utf8 col= late.icu.utf8 incremental_sort create_role without_overlaps generated_virt= ual +test: create_table_like alter_generic alter_operator misc async dbsize me= rge 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 (Postgre= SQL) package. +# +# Tom Lane , 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 \n" +"Language-Team: PgSQL-es-Ayuda \n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=3DUTF-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=C3=A1lida para tipo %s: =C2=AB%s=C2= =BB" + +#: 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=C3=B3n de origen =C2=AB%s=C2=BB no es v=C3=A1lida" + +#: regress.c:1033 +#, c-format +msgid "invalid destination encoding name \"%s\"" +msgstr "la codificaci=C3=B3n de destino =C2=AB%s=C2=BB no es v=C3=A1lida" + +#: regress.c:1078 +#, c-format +msgid "default conversion function for encoding \"%s\" to \"%s\" does not= exist" +msgstr "no existe el procedimiento por omisi=C3=B3n de conversi=C3=B3n de= sde la codificaci=C3=B3n =C2=AB%s=C2=BB a =C2=AB%s=C2=BB" + +#: 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=C3=B3= n." + +#: regress.c:1175 +#, c-format +msgid "translated PRId64 =3D %" +msgstr "traducido PRId64 =3D %" + +#: regress.c:1177 +#, c-format +msgid "translated PRId32 =3D %" +msgstr "traducido PRId32 =3D %" + +#: regress.c:1179 +#, c-format +msgid "translated PRIdMAX =3D %" +msgstr "traducido PRIdMAX =3D %" + +#: regress.c:1181 +#, c-format +msgid "translated PRIdPTR =3D %" +msgstr "traducido PRIdPTR =3D %" + +#: regress.c:1184 +#, c-format +msgid "translated PRIu64 =3D %" +msgstr "traducido PRIu64 =3D %" + +#: regress.c:1186 +#, c-format +msgid "translated PRIu32 =3D %" +msgstr "traducido PRIu32 =3D %" + +#: regress.c:1188 +#, c-format +msgid "translated PRIuMAX =3D %" +msgstr "traducido PRIuMAX =3D %" + +#: regress.c:1190 +#, c-format +msgid "translated PRIuPTR =3D %" +msgstr "traducido PRIuPTR =3D %" + +#: regress.c:1193 +#, c-format +msgid "translated PRIx64 =3D %" +msgstr "traducido PRIx64 =3D %" + +#: regress.c:1195 +#, c-format +msgid "translated PRIx32 =3D %" +msgstr "traducido PRIx32 =3D %" + +#: regress.c:1197 +#, c-format +msgid "translated PRIxMAX =3D %" +msgstr "traducido PRIxMAX =3D %" + +#: regress.c:1199 +#, c-format +msgid "translated PRIxPTR =3D %" +msgstr "traducido PRIxPTR =3D %" + +#: regress.c:1202 +#, c-format +msgid "translated PRIX64 =3D %" +msgstr "traducido PRIX64 =3D %" + +#: regress.c:1204 +#, c-format +msgid "translated PRIX32 =3D %" +msgstr "traducido PRIX32 =3D %" + +#: regress.c:1206 +#, c-format +msgid "translated PRIXMAX =3D %" +msgstr "traducido PRIXMAX =3D %" + +#: regress.c:1208 +#, c-format +msgid "translated PRIXPTR =3D %" +msgstr "traducido PRIXPTR =3D %" diff --git a/src/test/regress/po/meson.build b/src/test/regress/po/meson.b= uild 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 +=3D [i18n.gettext('regress-' + pg_version_major.to_string())= ] diff --git a/src/test/regress/regress.c b/src/test/regress/regress.c index c27305cf10b..26ae0a6c787 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,76 @@ test_relpath(PG_FUNCTION_ARGS) = PG_RETURN_VOID(); } + +/* + * Simple test to verify NLS support, particularly that the PRI* macros w= ork. + */ +PG_FUNCTION_INFO_V1(test_translation); +Datum +test_translation(PG_FUNCTION_ARGS) +{ +#ifdef ENABLE_NLS + static bool inited =3D false; + + /* + * Ideally we'd do this bit in a _PG_init() hook. However, it seems bes= t + * that the Solaris hack only get applied in the nls.sql test, so it + * doesn't risk affecting other tests that load this module. + */ + if (!inited) + { + /* + * Solaris' built-in gettext is not bright about associating locales + * with message catalogs that are named after just the language. + * Apparently the customary workaround is for users to set the + * LANGUAGE environment variable to provide a mapping. Do so here to + * ensure that the nls.sql regression test will work. + */ +#if defined(__sun__) + setenv("LANGUAGE", "es_ES.UTF-8:es", 1); +#endif + pg_bindtextdomain(TEXTDOMAIN); + inited =3D true; + } + + ereport(NOTICE, + errmsg("translated PRId64 =3D %" PRId64, (int64) 424242424242)); + ereport(NOTICE, + errmsg("translated PRId32 =3D %" PRId32, (int32) -1234)); + ereport(NOTICE, + errmsg("translated PRIdMAX =3D %" PRIdMAX, (intmax_t) -5678)); + ereport(NOTICE, + errmsg("translated PRIdPTR =3D %" PRIdPTR, (intptr_t) 9999)); + + ereport(NOTICE, + errmsg("translated PRIu64 =3D %" PRIu64, (uint64) 424242424242)); + ereport(NOTICE, + errmsg("translated PRIu32 =3D %" PRIu32, (uint32) 1234)); + ereport(NOTICE, + errmsg("translated PRIuMAX =3D %" PRIuMAX, (uintmax_t) 5678)); + ereport(NOTICE, + errmsg("translated PRIuPTR =3D %" PRIuPTR, (uintptr_t) 9999)); + + ereport(NOTICE, + errmsg("translated PRIx64 =3D %" PRIx64, (uint64) 424242424242)); + ereport(NOTICE, + errmsg("translated PRIx32 =3D %" PRIx32, (uint32) 1234)); + ereport(NOTICE, + errmsg("translated PRIxMAX =3D %" PRIxMAX, (uintmax_t) 5678)); + ereport(NOTICE, + errmsg("translated PRIxPTR =3D %" PRIxPTR, (uintptr_t) 9999)); + + ereport(NOTICE, + errmsg("translated PRIX64 =3D %" PRIX64, (uint64) 424242424242)); + ereport(NOTICE, + errmsg("translated PRIX32 =3D %" PRIX32, (uint32) 1234)); + ereport(NOTICE, + errmsg("translated PRIXMAX =3D %" PRIXMAX, (uintmax_t) 5678)); + ereport(NOTICE, + errmsg("translated PRIXPTR =3D %" 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..efeda8c5841 --- /dev/null +++ b/src/test/regress/sql/nls.sql @@ -0,0 +1,19 @@ +-- 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; + +-- Some BSDen are sticky about wanting a codeset name in lc_messages, +-- but it seems that at least on common platforms it doesn't have +-- to match the actual database encoding. +SET lc_messages =3D 'es_ES.UTF-8'; + +SELECT test_translation(); + +RESET lc_messages; -- = 2.43.7 ------- =_aaaaaaaaaa0--