From 9924f0432bb5e0dbdb46cd780f20d14c398baac6 Mon Sep 17 00:00:00 2001
From: Jacob Champion <jacob.champion@enterprisedb.com>
Date: Thu, 13 Nov 2025 12:42:00 -0800
Subject: [PATCH v3 1/3] libpq: Add missing OAuth translations
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Several strings that should have been translated as they passed through
libpq_gettext were not actually being pulled into the translation files,
because I hadn't directly wrapped them in one of the GETTEXT_TRIGGERS.

Move the responsibility for calling libpq_gettext() to the code that
sets actx->errctx. Doing the same in report_type_mismatch() would result
in double-translation, so mark those strings with gettext_noop()
instead. And wrap two ternary operands with gettext_noop(), even though
they're already in one of the triggers, since xgettext sees only the
first.

Finally, fe-auth-oauth.c was missing from nls.mk, so none of that file
was being translated at all. Add it now.

Original patch by Zhijie Hou, plus suggested tweaks by Álvaro Herrera
and small additions by me.

Reported-by: Zhijie Hou <houzj.fnst@fujitsu.com>
Author: Zhijie Hou <houzj.fnst@fujitsu.com>
Co-authored-by: Álvaro Herrera <alvherre@kurilemu.de>
Co-authored-by: Jacob Champion <jacob.champion@enterprisedb.com>
Reviewed-by: Álvaro Herrera <alvherre@kurilemu.de>
Discussion: https://postgr.es/m/TY4PR01MB1690746DB91991D1E9A47F57E94CDA%40TY4PR01MB16907.jpnprd01.prod.outlook.com
Backpatch-through: 18
---
 src/interfaces/libpq-oauth/oauth-curl.c | 35 +++++++++++++------------
 src/interfaces/libpq/nls.mk             |  1 +
 2 files changed, 19 insertions(+), 17 deletions(-)

diff --git a/src/interfaces/libpq-oauth/oauth-curl.c b/src/interfaces/libpq-oauth/oauth-curl.c
index bd0a656a166..c9765159260 100644
--- a/src/interfaces/libpq-oauth/oauth-curl.c
+++ b/src/interfaces/libpq-oauth/oauth-curl.c
@@ -247,7 +247,8 @@ struct async_ctx
 	 * our entry point, errors have three parts:
 	 *
 	 * - errctx:	an optional static string, describing the global operation
-	 *				currently in progress. It'll be translated for you.
+	 *				currently in progress. Should be translated with
+	 *				libpq_gettext().
 	 *
 	 * - errbuf:	contains the actual error message. Generally speaking, use
 	 *				actx_error[_str] to manipulate this. This must be filled
@@ -475,20 +476,20 @@ report_type_mismatch(struct oauth_parse *ctx)
 	switch (ctx->active->type)
 	{
 		case JSON_TOKEN_STRING:
-			msgfmt = "field \"%s\" must be a string";
+			msgfmt = gettext_noop("field \"%s\" must be a string");
 			break;
 
 		case JSON_TOKEN_NUMBER:
-			msgfmt = "field \"%s\" must be a number";
+			msgfmt = gettext_noop("field \"%s\" must be a number");
 			break;
 
 		case JSON_TOKEN_ARRAY_START:
-			msgfmt = "field \"%s\" must be an array of strings";
+			msgfmt = gettext_noop("field \"%s\" must be an array of strings");
 			break;
 
 		default:
 			Assert(false);
-			msgfmt = "field \"%s\" has unexpected type";
+			msgfmt = gettext_noop("field \"%s\" has unexpected type");
 	}
 
 	oauth_parse_set_error(ctx, msgfmt, ctx->active->name);
@@ -1087,7 +1088,7 @@ parse_token_error(struct async_ctx *actx, struct token_error *err)
 	 * override the errctx if parsing explicitly fails.
 	 */
 	if (!result)
-		actx->errctx = "failed to parse token error response";
+		actx->errctx = libpq_gettext("failed to parse token error response");
 
 	return result;
 }
@@ -1115,8 +1116,8 @@ record_token_error(struct async_ctx *actx, const struct token_error *err)
 		if (response_code == 401)
 		{
 			actx_error(actx, actx->used_basic_auth
-					   ? "provider rejected the oauth_client_secret"
-					   : "provider requires client authentication, and no oauth_client_secret is set");
+					   ? gettext_noop("provider rejected the oauth_client_secret")
+					   : gettext_noop("provider requires client authentication, and no oauth_client_secret is set"));
 			actx_error_str(actx, " ");
 		}
 	}
@@ -2153,7 +2154,7 @@ finish_discovery(struct async_ctx *actx)
 	/*
 	 * Pull the fields we care about from the document.
 	 */
-	actx->errctx = "failed to parse OpenID discovery document";
+	actx->errctx = libpq_gettext("failed to parse OpenID discovery document");
 	if (!parse_provider(actx, &actx->provider))
 		return false;			/* error message already set */
 
@@ -2421,7 +2422,7 @@ finish_device_authz(struct async_ctx *actx)
 	 */
 	if (response_code == 200)
 	{
-		actx->errctx = "failed to parse device authorization";
+		actx->errctx = libpq_gettext("failed to parse device authorization");
 		if (!parse_device_authz(actx, &actx->authz))
 			return false;		/* error message already set */
 
@@ -2509,7 +2510,7 @@ finish_token_request(struct async_ctx *actx, struct token *tok)
 	 */
 	if (response_code == 200)
 	{
-		actx->errctx = "failed to parse access token response";
+		actx->errctx = libpq_gettext("failed to parse access token response");
 		if (!parse_access_token(actx, tok))
 			return false;		/* error message already set */
 
@@ -2888,7 +2889,7 @@ pg_fe_run_oauth_flow_impl(PGconn *conn)
 		switch (actx->step)
 		{
 			case OAUTH_STEP_INIT:
-				actx->errctx = "failed to fetch OpenID discovery document";
+				actx->errctx = libpq_gettext("failed to fetch OpenID discovery document");
 				if (!start_discovery(actx, conn_oauth_discovery_uri(conn)))
 					goto error_return;
 
@@ -2902,11 +2903,11 @@ pg_fe_run_oauth_flow_impl(PGconn *conn)
 				if (!check_issuer(actx, conn))
 					goto error_return;
 
-				actx->errctx = "cannot run OAuth device authorization";
+				actx->errctx = libpq_gettext("cannot run OAuth device authorization");
 				if (!check_for_device_flow(actx))
 					goto error_return;
 
-				actx->errctx = "failed to obtain device authorization";
+				actx->errctx = libpq_gettext("failed to obtain device authorization");
 				if (!start_device_authz(actx, conn))
 					goto error_return;
 
@@ -2917,7 +2918,7 @@ pg_fe_run_oauth_flow_impl(PGconn *conn)
 				if (!finish_device_authz(actx))
 					goto error_return;
 
-				actx->errctx = "failed to obtain access token";
+				actx->errctx = libpq_gettext("failed to obtain access token");
 				if (!start_token_request(actx, conn))
 					goto error_return;
 
@@ -2968,7 +2969,7 @@ pg_fe_run_oauth_flow_impl(PGconn *conn)
 				break;
 
 			case OAUTH_STEP_WAIT_INTERVAL:
-				actx->errctx = "failed to obtain access token";
+				actx->errctx = libpq_gettext("failed to obtain access token");
 				if (!start_token_request(actx, conn))
 					goto error_return;
 
@@ -2994,7 +2995,7 @@ error_return:
 	 * also the documentation for struct async_ctx.
 	 */
 	if (actx->errctx)
-		appendPQExpBuffer(errbuf, "%s: ", libpq_gettext(actx->errctx));
+		appendPQExpBuffer(errbuf, "%s: ", actx->errctx);
 
 	if (PQExpBufferDataBroken(actx->errbuf))
 		appendPQExpBufferStr(errbuf, libpq_gettext("out of memory"));
diff --git a/src/interfaces/libpq/nls.mk b/src/interfaces/libpq/nls.mk
index b87df277d93..111e4849ed5 100644
--- a/src/interfaces/libpq/nls.mk
+++ b/src/interfaces/libpq/nls.mk
@@ -1,6 +1,7 @@
 # src/interfaces/libpq/nls.mk
 CATALOG_NAME     = libpq
 GETTEXT_FILES    = fe-auth.c \
+                   fe-auth-oauth.c \
                    fe-auth-scram.c \
                    fe-cancel.c \
                    fe-connect.c \
-- 
2.34.1

