Technical Analysis: Redundant/Mis-use of _(x) gettext Macro
Core Problem
This thread begins with what appears to be a simple cleanup — removing seemingly unnecessary _() gettext macro wrappings around format strings that contain only "%s" — but evolves into a deeper discussion about internationalization (i18n) correctness in PostgreSQL's publication name list construction.
Initial Misunderstanding
The original report identified two instances where _() wraps a format string containing nothing but a quoted format specifier (e.g., _("\"%s\"")). The assumption was that since there's "nothing to translate," the _() macro is superfluous. This is incorrect.
Why the _() Macro is Necessary on Quote-Only Strings
The _() macro marks strings for extraction into .po translation files. Even a string like "\"%s\"" — which appears to be "just quoting" — is legitimately translatable because:
-
Quotation marks are locale-dependent. Different languages use different quoting conventions:
- German:
»%s«(guillemets, reversed) - Spanish:
«%s»(guillemets) - French:
« %s »(guillemets with spaces) - English:
"%s"(double quotes)
- German:
-
Translators need control over punctuation surrounding interpolated values. This is documented in PostgreSQL's Error Style Guide.
The Real Bug: GetPublicationsStr Function
Once the initial misunderstanding was resolved, Peter Smith identified a genuine i18n defect in the GetPublicationsStr function (introduced in commit 8f2e2bb, ~4 years prior). This function builds comma-separated lists of publication names for two purposes:
- SQL construction (
quote_literal=true): Commas and quoting must be literal — no translation needed. - Error messages (
quote_literal=false): Commas and quoting SHOULD be translatable but were hardcoded.
This means for 4 years, error messages containing publication name lists have been un-translatable in their punctuation — a silent i18n regression.
Proposed Solutions
Patch v1
Separates the two code paths more clearly and wraps both the comma separator and the quotes in _() for the error-message path.
Patch v2
Removes translation of the comma separator alone (based on a separate discussion), keeping only quote translation.
Álvaro's Counter-Proposal
Álvaro Herrera argues that commas ARE translatable (Japanese uses 、 instead of ,; French adds spaces around separators). His approach combines the comma with the quoted item into a single translatable unit: _(", \"%s\"") — giving translators a single string that includes both the separator and the quoting, allowing them to adjust all punctuation holistically.
Design Debate: Leading vs. Trailing Comma
The remaining bikeshed is whether the translatable format should be:
- Leading comma (on non-first items):
_(", \"%s\"")— requires checkingfirstflag - Trailing comma (on non-last items):
_("\"%s\", ")— requires knowing list length or usingforeach_current_index
Peter Smith advocates for leading-comma with a first boolean because:
- No need to know list length up-front
- The string being built is always "valid" (no dangling comma)
David Rowley suggests trailing comma with foreach_current_index(lc) > 0 check.
Álvaro initially used "last" logic but expressed no strong preference.
Architectural Implications
This is a microcosm of a broader i18n principle in PostgreSQL: any string that reaches the user through ereport/elog must have ALL its visible characters under translator control. This includes:
- Quotation marks around identifiers
- List separators (commas, semicolons)
- Spacing around punctuation
The fix is small but sets a precedent for how list-building helper functions should handle i18n — by bundling separator punctuation into the translatable format string rather than concatenating it separately.
Current Status
As of the final message (2026-05-18), the patch is stalled. All participants agree on the need for the fix; the only open question is the cosmetic choice of leading vs. trailing comma in the format string. No committer has picked it up for commit.