From 8c498479d70f963533d57d8bb1b3a58e00fe0d03 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sun, 14 Dec 2025 11:55:18 -0500 Subject: [PATCH] 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 Discussion: https://postgr.es/m/292844.1765315339@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 = 'es_ES.UTF-8'; +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..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 = '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 += { '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 89bde9a2850..905f9bca959 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 , 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=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 = %" +msgstr "traducido PRId64 = %" + +#: regress.c:1177 +#, c-format +msgid "translated PRId32 = %" +msgstr "traducido PRId32 = %" + +#: regress.c:1179 +#, c-format +msgid "translated PRIdMAX = %" +msgstr "traducido PRIdMAX = %" + +#: regress.c:1181 +#, c-format +msgid "translated PRIdPTR = %" +msgstr "traducido PRIdPTR = %" + +#: regress.c:1184 +#, c-format +msgid "translated PRIu64 = %" +msgstr "traducido PRIu64 = %" + +#: regress.c:1186 +#, c-format +msgid "translated PRIu32 = %" +msgstr "traducido PRIu32 = %" + +#: regress.c:1188 +#, c-format +msgid "translated PRIuMAX = %" +msgstr "traducido PRIuMAX = %" + +#: regress.c:1190 +#, c-format +msgid "translated PRIuPTR = %" +msgstr "traducido PRIuPTR = %" + +#: regress.c:1193 +#, c-format +msgid "translated PRIx64 = %" +msgstr "traducido PRIx64 = %" + +#: regress.c:1195 +#, c-format +msgid "translated PRIx32 = %" +msgstr "traducido PRIx32 = %" + +#: regress.c:1197 +#, c-format +msgid "translated PRIxMAX = %" +msgstr "traducido PRIxMAX = %" + +#: regress.c:1199 +#, c-format +msgid "translated PRIxPTR = %" +msgstr "traducido PRIxPTR = %" + +#: regress.c:1202 +#, c-format +msgid "translated PRIX64 = %" +msgstr "traducido PRIX64 = %" + +#: regress.c:1204 +#, c-format +msgid "translated PRIX32 = %" +msgstr "traducido PRIX32 = %" + +#: regress.c:1206 +#, c-format +msgid "translated PRIXMAX = %" +msgstr "traducido PRIXMAX = %" + +#: regress.c:1208 +#, c-format +msgid "translated PRIXPTR = %" +msgstr "traducido 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 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 work. + */ +PG_FUNCTION_INFO_V1(test_translation); +Datum +test_translation(PG_FUNCTION_ARGS) +{ +#ifdef ENABLE_NLS + static bool inited = false; + + /* + * Ideally we'd do this bit in a _PG_init() hook. However, it seems best + * 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 = 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..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 = 'es_ES.UTF-8'; + +SELECT test_translation(); + +RESET lc_messages; -- 2.47.3