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