]> git.ipfire.org Git - thirdparty/gettext.git/commitdiff
OCaml support: Add OCaml support in xgettext.
authorBruno Haible <bruno@clisp.org>
Wed, 23 Jul 2025 07:37:23 +0000 (09:37 +0200)
committerBruno Haible <bruno@clisp.org>
Wed, 23 Jul 2025 09:30:27 +0000 (11:30 +0200)
* gettext-tools/doc/lang-ocaml.texi: New file.
* gettext-tools/doc/Makefile.am (gettext_TEXINFOS): Add it.
* gettext-tools/doc/gettext.texi (No string concatenation): Mention string
concatenation in OCaml.
(List of Programming Languages): Include lang-ocaml.texi.
* gettext-tools/doc/xgettext.texi: Document the -L OCaml option.
* gettext-tools/src/x-ocaml.h: New file.
* gettext-tools/src/x-ocaml.c: New file.
* gettext-tools/src/xgettext.c: Include x-ocaml.h.
(flag_table_ocaml): New variable.
(main): Invoke init_flag_table_ocaml, x_ocaml_extract_all, x_ocaml_keyword.
(usage): Document the -L OCaml option.
(language_to_extractor, extension_to_language): Support OCaml.
* gettext-tools/src/FILES: Mention x-ocaml.h, x-ocaml.c.
* gettext-tools/src/Makefile.am (noinst_HEADERS): Add x-ocaml.h.
(xgettext_SOURCES): Add x-ocaml.c.
* gettext-tools/po/POTFILES.in: Add src/x-ocaml.c.

gettext-tools/doc/Makefile.am
gettext-tools/doc/gettext.texi
gettext-tools/doc/lang-ocaml.texi [new file with mode: 0644]
gettext-tools/doc/xgettext.texi
gettext-tools/po/POTFILES.in
gettext-tools/src/FILES
gettext-tools/src/Makefile.am
gettext-tools/src/x-ocaml.c [new file with mode: 0644]
gettext-tools/src/x-ocaml.h [new file with mode: 0644]
gettext-tools/src/xgettext.c

index e725e430f9b01ba009a2928041543c4f746c6b72..9162a8f12d6ace575a853027d4d6b6516efeeff4 100644 (file)
@@ -85,6 +85,7 @@ gettext_TEXINFOS = \
   lang-pascal.texi \
   lang-modula2.texi \
   lang-d.texi \
+  lang-ocaml.texi \
   lang-smalltalk.texi \
   lang-vala.texi \
   lang-wxwidgets.texi \
index 4fd819e9e8e2abec87be6d7a4baa4e52d66aff81..9b5018f35bf092715c8a9a3047e0a1848608c4d4 100644 (file)
@@ -461,6 +461,7 @@ Individual Programming Languages
 * Lua::                         Lua
 * Pascal::                      Pascal - Free Pascal Compiler
 * Modula-2::                    Modula-2
+* OCaml::                       OCaml
 * Smalltalk::                   GNU Smalltalk
 * Vala::                        Vala
 * wxWidgets::                   wxWidgets library
@@ -2522,6 +2523,7 @@ at runtime (or possibly at compile time, if the compiler supports that).
 @cindex Lua, string concatenation
 @cindex Modula-2, string concatenation
 @cindex D, string concatenation
+@cindex OCaml, string concatenation
 @cindex Smalltalk, string concatenation
 @cindex Vala, string concatenation
 @cindex Perl, string concatenation
@@ -2567,6 +2569,9 @@ In Modula-2, string concatenation is denoted by the @samp{+} operator.
 In D, string concatenation is denoted by the @samp{~} operator.
 @c Reference: https://dlang.org/spec/expression.html#cat_expressions
 @item
+In OCaml, string concatenation is denoted by the @samp{^} operator.
+@c Reference: https://ocaml.org/manual/5.3/api/String.html#concat
+@item
 In Smalltalk, string concatenation is denoted by the @samp{,} operator.
 @c Reference: https://rmod-files.lille.inria.fr/FreeBooks/ByExample/14%20-%20Chapter%2012%20-%20Strings.pdf
 @item
@@ -10789,6 +10794,7 @@ that language, and to combine the resulting files using @code{msgcat}.
 * Pascal::                      Pascal - Free Pascal Compiler
 * Modula-2::                    Modula-2
 * D::                           D
+* OCaml::                       OCaml
 * Smalltalk::                   GNU Smalltalk
 * Vala::                        Vala
 * wxWidgets::                   wxWidgets library
@@ -10821,6 +10827,7 @@ that language, and to combine the resulting files using @code{msgcat}.
 @include lang-pascal.texi
 @include lang-modula2.texi
 @include lang-d.texi
+@include lang-ocaml.texi
 @include lang-smalltalk.texi
 @include lang-vala.texi
 @include lang-wxwidgets.texi
diff --git a/gettext-tools/doc/lang-ocaml.texi b/gettext-tools/doc/lang-ocaml.texi
new file mode 100644 (file)
index 0000000..24e76a9
--- /dev/null
@@ -0,0 +1,59 @@
+@c This file is part of the GNU gettext manual.
+@c Copyright (C) 1995-2025 Free Software Foundation, Inc.
+@c See the file gettext.texi for copying conditions.
+
+@node OCaml
+@subsection OCaml
+@cindex OCaml
+
+@table @asis
+@item RPMs
+ocaml, ocaml-gettext-devel
+
+@item Ubuntu packages
+ocaml, opam
+@*@code{opam install gettext-stub}
+
+@item File extension
+@code{ml}
+
+@item String syntax
+@code{"abc"}
+
+@item gettext shorthand
+@code{(s_ "abc")} or, for format strings, @code{(f_ "abc")}
+
+@item gettext/ngettext functions
+@code{s_}, @code{f_}, @code{sn_}, @code{fn_}
+
+@item textdomain
+@code{textdomain} field in first parameter of @code{Gettext.Program}
+
+@item bindtextdomain
+@code{dir} field in first parameter of @code{Gettext.Program}
+
+@item setlocale
+---
+
+@item Prerequisite
+Declare the libraries @code{gettext.base} and @code{gettext-stub}
+in the @samp{dune} file.
+
+@item Use or emulate GNU gettext
+Use (assuming that you pass @code{GettextStub.Native}
+as second parameter of @code{Gettext.Program})
+
+@item Extractor
+@code{xgettext}
+
+@item Formatting with positions
+---
+
+@item Portability
+fully portable
+
+@item po-mode marking
+---
+@end table
+
+@c An example is available in the @file{examples} directory: @code{hello-ocaml}.
index 72063c22644cfce36b4159609b03b0b0dc95a09d..423051bde148e52ae281db53469d6cdabf018d9c 100644 (file)
@@ -95,6 +95,7 @@ Specifies the language of the input files.  The supported languages are
 @code{Lua},
 @code{Modula-2},
 @code{D},
+@code{OCaml},
 @code{Smalltalk},
 @code{Vala},
 @code{Tcl},
index cea4a4176bc2a33779d5048bccee34947e50bed8..0a2f0e1a97e2a2dddc5973568b58e38e664adda6 100644 (file)
@@ -112,6 +112,7 @@ src/x-librep.c
 src/x-lisp.c
 src/x-lua.c
 src/x-modula2.c
+src/x-ocaml.c
 src/x-perl.c
 src/x-php.c
 src/x-po.c
index 9d900a867c24c7cf011ad181c5ebc1a4a47b89dd..b34f0cee30bb095aa9c58940841872721f4290e2 100644 (file)
@@ -422,6 +422,9 @@ msgl-check.c
 | x-d.c
 | html5-entities.h
 |               String extractor for D.
+| x-ocaml.h
+| x-ocaml.c
+|               String extractor for OCaml.
 | x-smalltalk.h
 | x-smalltalk.c
 |               String extractor for Smalltalk.
index 1b7faf125cbd6c441c9483e9d721e17a487f1bd1..0e66d1571b0f916a16b2d7f835b1da7e47ef0136 100644 (file)
@@ -91,6 +91,7 @@ noinst_HEADERS = \
   x-lua.h \
   x-modula2.h \
   x-d.h html5-entities.h \
+  x-ocaml.h \
   x-smalltalk.h \
   x-vala.h \
   x-tcl.h \
@@ -333,6 +334,7 @@ xgettext_SOURCES += \
   x-lua.c \
   x-modula2.c \
   x-d.c \
+  x-ocaml.c \
   x-smalltalk.c \
   x-vala.c \
   x-tcl.c \
diff --git a/gettext-tools/src/x-ocaml.c b/gettext-tools/src/x-ocaml.c
new file mode 100644 (file)
index 0000000..2a6b021
--- /dev/null
@@ -0,0 +1,142 @@
+/* xgettext OCaml backend.
+   Copyright (C) 2020-2025 Free Software Foundation, Inc.
+   Written by Bruno Haible <bruno@clisp.org>, 2025.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification.  */
+#include "x-ocaml.h"
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <error.h>
+#include "message.h"
+#include "clean-temp.h"
+#include "concat-filename.h"
+#include "execute.h"
+#include "xvasprintf.h"
+#include "x-po.h"
+#include "xgettext.h"
+#include "gettext.h"
+
+/* A convenience macro.  I don't like writing gettext() every time.  */
+#define _(str) gettext (str)
+
+/* We don't parse OCaml directly, but instead rely on the 'ocaml-gettext'
+   program, that invokes the 'ocaml-xgettext' program.  Both are contained
+   in the 'opam' package named 'gettext':
+     https://opam.ocaml.org/packages/gettext/
+     https://github.com/gildor478/ocaml-gettext
+     https://github.com/gildor478/ocaml-gettext/blob/master/doc/reference-manual.md
+
+   Comments start with '(*' and end with '*)' and can be nested.
+   Reference: <https://ocaml.org/docs/tour-of-ocaml>
+ */
+
+
+/* ====================== Keyword set customization.  ====================== */
+
+/* This function currently has no effect.  */
+void
+x_ocaml_extract_all (void)
+{
+}
+
+/* This function currently has no effect.  */
+void
+x_ocaml_keyword (const char *keyword)
+{
+}
+
+/* This function currently has no effect.  */
+void
+init_flag_table_ocaml (void)
+{
+}
+
+
+/* ========================= Extracting strings.  ========================== */
+
+static bool
+is_not_header (const message_ty *mp)
+{
+  return !is_header (mp);
+}
+
+void
+extract_ocaml (const char *found_in_dir, const char *real_filename,
+               const char *logical_filename,
+               flag_context_list_table_ty *flag_table,
+               msgdomain_list_ty *mdlp)
+{
+  /* Invoke
+       ocaml-gettext --action extract --extract-pot <temp>.pot real_filename  */
+
+  /* First, create a temporary directory where this invocation can place its
+     output.  */
+  struct temp_dir *tmpdir = create_temp_dir ("ocgt", NULL, false);
+  if (tmpdir == NULL)
+    exit (EXIT_FAILURE);
+
+  /* Prepare the temporary POT file name.  */
+  char *temp_file_name = xconcatenated_filename (tmpdir->dir_name, "temp.pot", NULL);
+  register_temp_file (tmpdir, temp_file_name);
+
+  /* Invoke ocaml-gettext.  */
+  const char *progname = "ocaml-gettext";
+  {
+    const char *argv[7];
+    int exitstatus;
+    /* Prepare arguments.  */
+    argv[0] = progname;
+    argv[1] = "--action";
+    argv[2] = "extract";
+    argv[3] = "--extract-pot";
+    argv[4] = temp_file_name;
+    argv[5] = logical_filename;
+    argv[6] = NULL;
+    exitstatus = execute (progname, progname, argv, NULL, found_in_dir,
+                          true, false, false, false, true, false, NULL);
+    if (exitstatus != 0)
+      error (EXIT_FAILURE, 0, _("%s subprocess failed with exit code %d"),
+             progname, exitstatus);
+  }
+
+  /* Read the resulting POT file.  */
+  {
+    FILE *fp = fopen (temp_file_name, "r");
+    if (fp == NULL)
+      error (EXIT_FAILURE, 0, _("%s subprocess did not create the expected file"),
+             progname);
+    char *dummy_filename = xasprintf (_("(output from '%s')"), progname);
+    extract_po (fp, temp_file_name, dummy_filename, flag_table, mdlp);
+    fclose (fp);
+    free (dummy_filename);
+  }
+
+  cleanup_temp_dir (tmpdir);
+
+  if (xgettext_omit_header)
+    {
+      /* Remove the header entry.  */
+      if (mdlp->nitems > 0)
+        message_list_remove_if_not (mdlp->item[0]->messages, is_not_header);
+    }
+}
diff --git a/gettext-tools/src/x-ocaml.h b/gettext-tools/src/x-ocaml.h
new file mode 100644 (file)
index 0000000..2efa457
--- /dev/null
@@ -0,0 +1,50 @@
+/* xgettext OCaml backend.
+   Copyright (C) 2020-2025 Free Software Foundation, Inc.
+   Written by Bruno Haible <bruno@clisp.org>, 2025.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+
+#include <stdio.h>
+
+#include "message.h"
+#include "xg-arglist-context.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define EXTENSIONS_OCAML \
+  { "ml",     "OCaml"  },                                               \
+
+#define SCANNERS_OCAML \
+  { "OCaml",            NULL, extract_ocaml,                            \
+                        NULL, NULL, NULL },                             \
+
+extern void extract_ocaml (const char *found_in_dir, const char *real_filename,
+                           const char *logical_filename,
+                           flag_context_list_table_ty *flag_table,
+                           msgdomain_list_ty *mdlp);
+
+extern void x_ocaml_keyword (const char *keyword);
+extern void x_ocaml_extract_all (void);
+
+extern void init_flag_table_ocaml (void);
+
+
+#ifdef __cplusplus
+}
+#endif
index 11a5c7c88eff5b236e68dad6a8aebaef5eb6fc8e..92b77add06b3fa0e164c9e53b00b98b3f337d9d8 100644 (file)
 #include "x-lua.h"
 #include "x-modula2.h"
 #include "x-d.h"
+#include "x-ocaml.h"
 #include "x-smalltalk.h"
 #include "x-vala.h"
 #include "x-tcl.h"
@@ -369,6 +370,7 @@ main (int argc, char *argv[])
   init_flag_table_lua ();
   init_flag_table_modula2 ();
   init_flag_table_d ();
+  init_flag_table_ocaml ();
   init_flag_table_vala ();
   init_flag_table_tcl ();
   init_flag_table_perl ();
@@ -466,6 +468,7 @@ main (int argc, char *argv[])
         x_lua_extract_all ();
         x_modula2_extract_all ();
         x_d_extract_all ();
+        x_ocaml_extract_all ();
         x_vala_extract_all ();
         x_tcl_extract_all ();
         x_perl_extract_all ();
@@ -552,6 +555,7 @@ main (int argc, char *argv[])
         x_lua_keyword (optarg);
         x_modula2_keyword (optarg);
         x_d_keyword (optarg);
+        x_ocaml_keyword (optarg);
         x_vala_keyword (optarg);
         x_tcl_keyword (optarg);
         x_perl_keyword (optarg);
@@ -1185,7 +1189,7 @@ Choice of input file language:\n"));
                                 (C, C++, ObjectiveC, PO, Python, Java,\n\
                                 JavaProperties, C#, JavaScript, TypeScript, TSX,\n\
                                 Scheme, Guile, Lisp, EmacsLisp, librep, Rust,\n\
-                                Go, Ruby, Shell, awk, Lua, Modula-2, D,\n\
+                                Go, Ruby, Shell, awk, Lua, Modula-2, D, OCaml,\n\
                                 Smalltalk, Vala, Tcl, Perl, PHP, GCC-source,\n\
                                 YCP, NXStringTable, RST, RSJ, Glade, GSettings,\n\
                                 Desktop)\n"));
@@ -2513,6 +2517,7 @@ language_to_extractor (const char *name)
     SCANNERS_LUA
     SCANNERS_MODULA2
     SCANNERS_D
+    SCANNERS_OCAML
     SCANNERS_SMALLTALK
     SCANNERS_VALA
     SCANNERS_TCL
@@ -2612,6 +2617,7 @@ extension_to_language (const char *extension)
     EXTENSIONS_LUA
     EXTENSIONS_MODULA2
     EXTENSIONS_D
+    EXTENSIONS_OCAML
     EXTENSIONS_SMALLTALK
     EXTENSIONS_VALA
     EXTENSIONS_TCL