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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.