One of the main pains in translating PostgreSQL messages is translating
the SQL syntax synopses in psql.  Things like:

msgid ""
"[ WITH [ RECURSIVE ] with_query [, ...] ]\n"
"SELECT [ ALL | DISTINCT [ ON ( expression [, ...] ) ] ]\n"
"    * | expression [ [ AS ] output_name ] [, ...]\n"
"    INTO [ TEMPORARY | TEMP ] [ TABLE ] new_table\n"
"    [ FROM from_item [, ...] ]\n"
"    [ WHERE condition ]\n"
"    [ GROUP BY expression [, ...] ]\n"
"    [ HAVING condition [, ...] ]\n"
"    [ WINDOW window_name AS ( window_definition ) [, ...] ]\n"
"    [ { UNION | INTERSECT | EXCEPT } [ ALL ] select ]\n"
"    [ ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS
{ FIRST | "
"LAST } ] [, ...] ]\n"
"    [ LIMIT { count | ALL } ]\n"
"    [ OFFSET start [ ROW | ROWS ] ]\n"
"    [ FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } ONLY ]\n"
"    [ FOR { UPDATE | SHARE } [ OF table_name [, ...] ] [ NOWAIT ]
[...] ]"

Especially when small things are changed from release to release,
figuring this out on the part of the translator is cumbersome and
error-prone.

Instead of translating the whole string, that is (picking a shorter
example)

N_("ALTER TEXT SEARCH PARSER name RENAME TO newname")

we really only want to translate the placeholders, so it could look like
this:

   appendPQExpBuffer(buf,
                      "ALTER TEXT SEARCH PARSER %s RENAME TO %s",
                      _("name"),
                      _("newname"));

This is what the attached patch produces.

Comments?
diff --git a/src/bin/psql/Makefile b/src/bin/psql/Makefile
index d6c3a93..9fc1511 100644
--- a/src/bin/psql/Makefile
+++ b/src/bin/psql/Makefile
@@ -22,6 +22,7 @@ override CPPFLAGS := -I. -I$(srcdir) -I$(libpq_srcdir) -I$(top_srcdir)/src/bin/p
 OBJS=	command.o common.o help.o input.o stringutils.o mainloop.o copy.o \
 	startup.o prompt.o variables.o large_obj.o print.o describe.o \
 	psqlscan.o tab-complete.o mbprint.o dumputils.o keywords.o kwlookup.o \
+	sql_help.o \
 	$(WIN32RES)
 
 FLEXFLAGS = -Cfe
@@ -40,8 +41,9 @@ dumputils.c keywords.c: % : $(top_srcdir)/src/bin/pg_dump/%
 kwlookup.c: % : $(top_srcdir)/src/backend/parser/%
 	rm -f $@ && $(LN_S) $< .
 
+sql_help.c: sql_help.h ;
 sql_help.h: create_help.pl $(wildcard $(REFDOCDIR)/*.sgml)
-	$(PERL) $< $(REFDOCDIR) $@
+	$(PERL) $< $(REFDOCDIR) $*
 
 psqlscan.c: psqlscan.l
 ifdef FLEX
@@ -67,4 +69,4 @@ clean distclean:
 	rm -f psql$(X) $(OBJS) dumputils.c keywords.c kwlookup.c
 
 maintainer-clean: distclean
-	rm -f sql_help.h psqlscan.c
+	rm -f sql_help.h sql_help.c psqlscan.c
diff --git a/src/bin/psql/create_help.pl b/src/bin/psql/create_help.pl
index ea0e89c..ef055f0 100644
--- a/src/bin/psql/create_help.pl
+++ b/src/bin/psql/create_help.pl
@@ -14,7 +14,7 @@
 # enough that this worked, but this here is by no means an SGML
 # parser.
 #
-# Call: perl create_help.pl docdir sql_help.h
+# Call: perl create_help.pl docdir sql_help
 # The name of the header file doesn't matter to this script, but it
 # sure does matter to the rest of the source.
 #
@@ -22,26 +22,29 @@
 use strict;
 
 my $docdir = $ARGV[0] or die "$0: missing required argument: docdir\n";
-my $outputfile = $ARGV[1] or die "$0: missing required argument: output file\n";
+my $hfile = $ARGV[1] . '.h' or die "$0: missing required argument: output file\n";
+my $cfile = $ARGV[1] . '.c';
 
-my $outputfilebasename;
-if ($outputfile =~ m!.*/([^/]+)$!) {
-    $outputfilebasename = $1;
+my $hfilebasename;
+if ($hfile =~ m!.*/([^/]+)$!) {
+    $hfilebasename = $1;
 }
 else {
-    $outputfilebasename = $outputfile;
+    $hfilebasename = $hfile;
 }
 
-my $define = $outputfilebasename;
+my $define = $hfilebasename;
 $define =~ tr/a-z/A-Z/;
 $define =~ s/\W/_/g;
 
 opendir(DIR, $docdir)
     or die "$0: could not open documentation source dir '$docdir': $!\n";
-open(OUT, ">$outputfile")
-    or die "$0: could not open output file '$outputfile': $!\n";
+open(HFILE, ">$hfile")
+    or die "$0: could not open output file '$hfile': $!\n";
+open(CFILE, ">$cfile")
+    or die "$0: could not open output file '$cfile': $!\n";
 
-print OUT
+print HFILE
 "/*
  * *** Do not change this file by hand. It is automatically
  * *** generated from the DocBook documentation.
@@ -56,15 +59,30 @@ print OUT
 
 #define N_(x) (x)				/* gettext noop */
 
+#include \"postgres_fe.h\"
+#include \"pqexpbuffer.h\"
+
 struct _helpStruct
 {
 	const char	   *cmd;		/* the command name */
 	const char	   *help;		/* the help associated with it */
-	const char	   *syntax;		/* the syntax associated with it */
+	void (* const syntaxfunc)(PQExpBuffer);	/* function that prints the syntax associated with it */
 };
 
+";
+
+print CFILE
+"/*
+ * *** Do not change this file by hand. It is automatically
+ * *** generated from the DocBook documentation.
+ *
+ * generated by
+ *     $^X $0 @ARGV
+ *
+ */
+
+#include \"$hfile\"
 
-static const struct _helpStruct QL_HELP[] = {
 ";
 
 my $maxlen = 0;
@@ -95,12 +113,18 @@ foreach my $file (sort readdir DIR) {
 	$cmddesc =~ s/\s+/ /g;
         $cmddesc =~ s/\"/\\"/g;
 
-	$cmdsynopsis =~ s/<[^>]+>//g;
+        my @params = ();
+
+        while ($cmdsynopsis =~ m!<[^>]+>([^<]+)</[^>]+>!) {
+            my $match = $1;
+            push @params, $match;
+            $cmdsynopsis =~ s!<[^>]+>[^<]+</[^>]+>!%s!;
+        }
 	$cmdsynopsis =~ s/\r?\n/\\n/g;
         $cmdsynopsis =~ s/\"/\\"/g;
 
         foreach my $cmdname (@cmdnames) {
-	    $entries{$cmdname} = { cmddesc => $cmddesc, cmdsynopsis => $cmdsynopsis };
+	    $entries{$cmdname} = { cmddesc => $cmddesc, cmdsynopsis => $cmdsynopsis, params => \...@params };
 	    $maxlen = ($maxlen >= length $cmdname) ? $maxlen : length $cmdname;
 	}
     }
@@ -109,9 +133,40 @@ foreach my $file (sort readdir DIR) {
     }
 }
 
-print OUT "    { \"$_\",\n      N_(\"".$entries{$_}{cmddesc}."\"),\n      N_(\"".$entries{$_}{cmdsynopsis}."\") },\n\n" foreach (sort keys %entries);
+foreach (sort keys %entries) {
+    my $prefix = "\t"x5 . '  ';
+    my $id = $_;
+    $id =~ s/ /_/g;
+    my $synopsis = "\"$entries{$_}{cmdsynopsis}\"";
+    $synopsis =~ s/\\n/\\n"\n$prefix"/g;
+    my @args = ("buf",
+                $synopsis,
+                map("_(\"$_\")", @{$entries{$_}{params}}));
+    print HFILE "extern void sql_help_$id(PQExpBuffer buf);\n";
+    print CFILE "void
+sql_help_$id(PQExpBuffer buf)
+{
+\tappendPQExpBuffer(".join(",\n$prefix", @args).");
+}
+
+";
+}
+
+print HFILE "
+
+static const struct _helpStruct QL_HELP[] = {
+";
+foreach (sort keys %entries) {
+    my $id = $_;
+    $id =~ s/ /_/g;
+    print HFILE "    { \"$_\",
+      N_(\"$entries{$_}{cmddesc}\"),
+      sql_help_$id },
+
+";
+}
 
-print OUT "
+print HFILE "
     { NULL, NULL, NULL }    /* End of list marker */
 };
 
@@ -123,5 +178,6 @@ print OUT "
 #endif /* $define */
 ";
 
-close OUT;
+close CFILE;
+close HFILE;
 closedir DIR;
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 74c379f..c575cb5 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -387,9 +387,11 @@ helpSQL(const char *topic, unsigned short int pager)
 					strcmp(topic, "*") == 0)
 				{
 					nl_count += 5;
+#if FIXME
 					for (ch = QL_HELP[i].syntax; *ch != '\0'; ch++)
 						if (*ch == '\n')
 							nl_count++;
+#endif
 					/* If we have an exact match, exit.  Fixes \h SELECT */
 					if (pg_strcasecmp(topic, QL_HELP[i].cmd) == 0)
 						break;
@@ -403,13 +405,17 @@ helpSQL(const char *topic, unsigned short int pager)
 				if (pg_strncasecmp(topic, QL_HELP[i].cmd, len) == 0 ||
 					strcmp(topic, "*") == 0)
 				{
+					PQExpBufferData buffer;
+
+					initPQExpBuffer(&buffer);
+					QL_HELP[i].syntaxfunc(&buffer);
 					help_found = true;
 					fprintf(output, _("Command:     %s\n"
 									  "Description: %s\n"
 									  "Syntax:\n%s\n\n"),
 							QL_HELP[i].cmd,
 							_(QL_HELP[i].help),
-							_(QL_HELP[i].syntax));
+							buffer.data);
 					/* If we have an exact match, exit.  Fixes \h SELECT */
 					if (pg_strcasecmp(topic, QL_HELP[i].cmd) == 0)
 						break;
diff --git a/src/bin/psql/nls.mk b/src/bin/psql/nls.mk
index d466b67..cc93f54 100644
--- a/src/bin/psql/nls.mk
+++ b/src/bin/psql/nls.mk
@@ -2,6 +2,6 @@
 CATALOG_NAME	:= psql
 AVAIL_LANGUAGES	:= cs de es fr ja pt_BR sv tr
 GETTEXT_FILES	:= command.c common.c copy.c help.c input.c large_obj.c \
-                   mainloop.c print.c startup.c describe.c sql_help.h \
+                   mainloop.c print.c startup.c describe.c sql_help.h sql_help.c \
                    ../../port/exec.c
 GETTEXT_TRIGGERS:= _ N_ psql_error simple_prompt
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to