Re: [Dlang] Internationalisation WIP
Adela Vais <[email protected]>
| Newsgroups | gmane.comp.parsers.bison.patches |
|---|---|
| Message-ID | <CAPk8xGfEeS06mm1GVnfo82iZVgJESCM5uumG9LAOuTF+Erp6NQ@mail.gmail.com> |
Hello, I apparently did not run ./bootstrap after installing gettext on my system. Now the files are generated as they should be. Thank you, Akim, for the explanations! I have a question about Bison's (not the users') error messages, should they be internationalized by default? That was my approach. I consider the patch ready to be installed now. Adela În joi, 3 dec. 2020 la 08:08, Akim Demaille <[email protected]> a scris: > Hi Adela, > > > Le 2 déc. 2020 à 01:33, Adela Vais <[email protected]> a écrit : > > > > Hello! > > > > I never worked with gettext (as a bash command) before, and I have some > > trouble understanding how I automatically generate the .mo files and put > > them into /usr/local/share/locale (where the code from the > > examples/c/bisthromatic hints they should be found). > > I would not bother with this. The Makefile machinery is there for us > not to have to think about that. You will find the po files in po/ > and in runtime-po/. > > Let "make install" do its job. > > > > My understanding is > > that from the .po files I have to generate .mo files, and these are used > by > > the programs generated with Bison. > > Yes. But you should not have to do that by hand. Just install Bison. > > > > If I try to run "sudo make install" in the po directory I get this error: > > > > [...] > > /usr/bin/install: cannot stat './bg.gmo': No such file or directory > > installing ./bg.gmo as /usr/local/share/locale/bg/LC_MESSAGES/bison.mo > > [...] > > > > and so on for each language file. After the command, no files exist in > the > > LC_MESSAGES directories. > > I need more details, your logs are too short. What do you have in po/? > and in runtime-po? Did you properly run "./bootstrap"? > > > > If I run make update-po or update-gmo, I also get errors for missing > files. > > I need logs. > > > > The second problem is that I expected dgettext to receive the argument > > "bison-runtime", like in C, not "bison", as it happens now in D. If I use > > "bison-runtime" the messages remain in English. > > Did you check whether the PO files in bison-runtime do contain the > translations you depend upon? > > > > I don't know what you mean here. The reason the C skeleton uses > bison-runtime is because it does so explicitly: > > # if ENABLE_NLS > # include <libintl.h> /* INFRINGES ON USER NAME SPACE */ > # define YY_(Msgid) dgettext ("bison-runtime", Msgid) > # endif > > So all the messages generated by *bison* use YY_, i.e., translations > from the bison-runtime catalogue. While *user* messages are translated > as follows (from c/bistromathic): > > #if defined ENABLE_NLS && ENABLE_NLS > # define _(Msgid) gettext (Msgid) > #else > # define _(Msgid) (Msgid) > #endif > > static const char * > yysymbol_name (yysymbol_kind_t yysymbol) > { > static const char *const yy_sname[] = > { > N_("end of file"), N_("error"), N_("invalid token"), "+", "-", "*", "/", > "^", "(", ")", "=", "exit", N_("number"), N_("function"), N_("variable"), > "NEG", "$accept", "input", "exp", YY_NULLPTR > }; > /* YYTRANSLATABLE[SYMBOL-NUM] -- Whether YY_SNAME[SYMBOL-NUM] is > internationalizable. */ > static yytype_int8 yytranslatable[] = > { > 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, > 0, 0, 1, 1, 1, 0 > }; > return (yysymbol < YYNTOKENS && yytranslatable[yysymbol] > ? _(yy_sname[yysymbol]) <======================== > : yy_sname[yysymbol]); > } > > That means, of course, that both catalogues must be loaded. In the > bistromathic you'll see > > #if defined ENABLE_NLS && ENABLE_NLS > // Set up internationalization. > setlocale (LC_ALL, ""); > // Use Bison's standard translation catalogue for error messages > // (the generated messages). > bindtextdomain ("bison-runtime", BISON_LOCALEDIR); > // The translation catalogue of bistromathic is actually included in > // Bison's. In your own project, use the name of your project. > bindtextdomain ("bison", LOCALEDIR); > textdomain ("bison"); > #endif > > But you know all this, since you did it properly in your branch. > > > Here [1] (and also as attachments) is a work in progress for this task, > > with the mention that examples/d/c/calc/calc.y is modified just for > testing > > reasons, it will not be changed in the official commit. > > Let's address your issues one at a time. First, let's see why > make install fails for you. > > Cheers!
0001-d-add-internationalisation.patch
(application/octet-stream, 8.5 KB)
From 933a217b1296a4cc75982c9dd2a37e5393af525a Mon Sep 17 00:00:00 2001 From: Adela Vais <[email protected]> Date: Sat, 28 Nov 2020 21:09:39 +0200 Subject: [PATCH for Dlang support] d: add internationalisation The D parser implements this feature similarly to the C parser, by using gettext. gettext and dgettext are imported using extern(C). With internationalisation enabled, YYParser.SymbolKind.toString uses GC, because the message is parsed, saved in a string to be translated, and then returned via the sink, as opposed to being put in the sink while it is parsed. * data/skeletons/d.m4 (SymbolKind.yytranslatable: New), data/skeletons/lalr1.d: Here. * doc/bison.texi: Document it. * tests/calc.at: Test it. --- data/skeletons/d.m4 | 52 ++++++++++++++++++++++++++++++------ data/skeletons/lalr1.d | 60 +++++++++++++++++++++++++++++++++++------- doc/bison.texi | 49 ++++++++++++++++++++++++++++++++++ tests/calc.at | 13 +++++++++ 4 files changed, 157 insertions(+), 17 deletions(-) diff --git a/data/skeletons/d.m4 b/data/skeletons/d.m4 index b84d4e94..489dce46 100644 --- a/data/skeletons/d.m4 +++ b/data/skeletons/d.m4 @@ -192,7 +192,12 @@ b4_symbol_foreach([b4_token_enum])dnl } ]) - +# b4_symbol_translate(STRING) +# --------------------------- +# Used by "bison" in the array of symbol names to mark those that +# require translation. +m4_define([b4_symbol_translate], +[[_($1)]]) ## -------------- ## ## Symbol kinds. ## @@ -243,6 +248,12 @@ m4_define([b4_declare_symbol_enum], static immutable string[] yytname_ = @{ ]b4_tname[ @}; +]b4_has_translations_if([[ + /* YYTRANSLATABLE[SYMBOL-NUM] -- Whether YY_SNAME[SYMBOL-NUM] is + internationalizable. */ + static immutable ]b4_int_type_for([b4_translatable])[[] yytranslatable = @{ + ]b4_translatable[ + @};]])[ /* Return YYSTR after stripping away unnecessary quotes and backslashes, so that it's suitable for yyerror. The heuristic is @@ -252,8 +263,31 @@ m4_define([b4_declare_symbol_enum], final void toString(W)(W sink) const if (isOutputRange!(W, char)) { - string yystr = yytname_[yycode_]; - + string yystr = yytname_[yycode_];]b4_has_translations_if([[ + if (yystr[0] == '"') + { + string yyr; + strip_quotes: + for (int i = 1; i < yystr.length; i++) + switch (yystr[i]) + { + case '\'': + case ',': + break strip_quotes; + case '\\': + if (yystr[++i] != '\\') + break strip_quotes; + goto default; + default: + yyr ~= yystr[i]; + break; + case '"': + yyr = (yycode_ < yyntokens_ && yytranslatable[yycode_] > 0) + ? _(yyr) : yyr; + put(sink, yyr); + return; + } + }]],[[ if (yystr[0] == '"') { strip_quotes: @@ -275,13 +309,15 @@ m4_define([b4_declare_symbol_enum], case '"': return; } - } + }]])[ else if (yystr == "$end") - { - put(sink, "end of input"); + {]b4_has_translations_if([[ + put(sink, _("end of input"));]],[[ + put(sink, "end of input");]])[ return; - } - + }]b4_has_translations_if([[ + yystr = (yycode_ < yyntokens_ && yytranslatable[yycode_] > 0) + ? _(yystr) : yystr;]])[ put(sink, yystr); } } diff --git a/data/skeletons/lalr1.d b/data/skeletons/lalr1.d index d126d499..615482d4 100644 --- a/data/skeletons/lalr1.d +++ b/data/skeletons/lalr1.d @@ -40,7 +40,28 @@ version(D_Version2) { ]b4_user_post_prologue[ ]b4_percent_code_get([[imports]])[ import std.format; +import std.conv; +/** + * Handle error message internationalisation + */ +extern(C) char* dgettext(const char*, const char*); +string YY_(const char* s) +{ + char* res = dgettext("bison-runtime", s); + return to!string(res); +} +]b4_has_translations_if([ +/** + * User error message internationalisation + */ +extern(C) char* gettext(const char*); +string _(string s) +{ + char* res = gettext(s.ptr); + return to!string(res); +} +])[ /** * A Bison parser, automatically generated from <tt>]m4_bpatsubst(b4_file_name, [^"\(.*\)"$], [\1])[</tt>. * @@ -704,20 +725,41 @@ m4_popdef([b4_at_dollar])])dnl immutable int argmax = 5; SymbolKind[] yyarg = new SymbolKind[argmax]; int yycount = yysyntaxErrorArguments(yyctx, yyarg, argmax); - string res = "syntax error, unexpected "; - res ~= format!"%s"(yyarg[0]); - if (yycount < argmax + 1) + string[] yystr = new string[yycount]; + for (int yyi = 0; yyi < yycount; yyi++) + yystr[yyi] = format!"%s"(yyarg[yyi]); + string res, yyformat; + import std.string; + switch (yycount) { - for (int yyi = 1; yyi < yycount; yyi++) - { - res ~= yyi == 1 ? ", expecting " : " or "; - res ~= format!"%s"(SymbolKind(yyarg[yyi])); - } + case 1: + yyformat = YY_("syntax error, unexpected %s"); + res = format(yyformat, yystr[0]); + break; + case 2: + yyformat = YY_("syntax error, unexpected %s, expecting %s"); + res = format(yyformat, yystr[0], yystr[1]); + break; + case 3: + yyformat = YY_("syntax error, unexpected %s, expecting %s or %s"); + res = format(yyformat, yystr[0], yystr[1], yystr[2]); + break; + case 4: + yyformat = YY_("syntax error, unexpected %s, expecting %s or %s or %s"); + res = format(yyformat, yystr[0], yystr[1], yystr[2], yystr[3]); + break; + case 5: + yyformat = YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); + res = format(yyformat, yystr[0], yystr[1], yystr[2], yystr[3], yystr[4]); + break; + default: + res = YY_("syntax error"); + break; } yyerror(]b4_locations_if([yyctx.getLocation(), ])[res); }]], [[simple]], [[ - yyerror(]b4_locations_if([yyctx.getLocation(), ])["syntax error");]])[ + yyerror(]b4_locations_if([yyctx.getLocation(), ])[YY_("syntax error"));]])[ } ]b4_parse_error_bmatch( diff --git a/doc/bison.texi b/doc/bison.texi index 959a4039..3f3a8b26 100644 --- a/doc/bison.texi +++ b/doc/bison.texi @@ -13945,6 +13945,55 @@ or nonzero, full tracing. Identify the Bison version and skeleton used to generate this parser. @end deftypecv +The internationalization in D is very simmilar to the one in C. The D +parser uses gettext for user messages and dgettext for Bison messages. + +If gettext is not present on your system, install it and re-run Bison's +configuration. + +For enabling internationalisation, import bindtextdomain and textdomain +from C: + +@example +extern(C) char* bindtextdomain(const char* domainname, const char* dirname); +extern(C) char* textdomain(const char* domainname); +@end example + +The main function should load the translation catalogues, similarly to the +@file{c/bistromathic} example: + +@example +int main() +@{ + import core.stdc.locale; + + // Set up internationalization. + setlocale(LC_ALL, ""); + // Use Bison's standard translation catalogue for error messages + // (the generated messages). + bindtextdomain("bison-runtime", BISON_LOCALEDIR); + // For the translation catalogue of your own project, use the + // name of your project. + bindtextdomain("bison", LOCALEDIR); + textdomain("bison"); + + // usual main content + ... +@} +@end example + +String aliases may be marked for internationalization (@pxref{Token +I18n}): + +@example +%token PLUS "+" + MINUS "-" + STAR "*" + SLASH "/" + EOL _("end of line") +%token <ival> NUM _("number") +@end example + @node D Parser Context Interface @subsection D Parser Context Interface The parser context provides information to build error reports when you diff --git a/tests/calc.at b/tests/calc.at index c11ab5c3..1e343e8a 100644 --- a/tests/calc.at +++ b/tests/calc.at @@ -665,6 +665,19 @@ m4_define([_AT_DATA_CALC_Y(d)], }; %printer { fprintf (yyo, "%d", $$); } <ival>; +%code { +]AT_TOKEN_TRANSLATE_IF([[ + static string _(string s) + { + if (s == "end of input") + return "end of file"; + else if (s == "number") + return "nombre"; + return s; + } +]])[ +} + /* Bison Declarations */ %token EOF 0 ]AT_TOKEN_TRANSLATE_IF([_("end of file")], ["end of input"])[ %token <ival> NUM "number" -- 2.17.1