FLTK logo

[master] 2953187 - STR 3289: Fluid i18n, gettext, catguts improvements

FLTK matrix user chat room
(using Element browser app)   FLTK gitter user chat room   GitHub FLTK Project   FLTK News RSS Feed  
  FLTK Apps      FLTK Library      Forums      Links     Login 
 All Forums  |  Back to fltk.commit  ]
 
Previous Message ]Next Message ]

[master] 2953187 - STR 3289: Fluid i18n, gettext, catguts improvements "Matthias Melcher" Dec 18, 2021  
 
commit 29531873ea4395a2dc65cefcc28db09569c38c18
Author:     Matthias Melcher <git@matthiasm.com>
AuthorDate: Sun Dec 19 01:09:13 2021 +0100
Commit:     Matthias Melcher <github@matthiasm.com>
CommitDate: Sun Dec 19 02:03:05 2021 +0100

    STR 3289: Fluid i18n, gettext, catguts improvements
    
    Removed some unneeded code.

 documentation/src/fluid-gettext.png | Bin 4369 -> 32267 bytes
 documentation/src/fluid.dox         |  26 ++++-
 fluid/Fl_Menu_Type.cxx              | 186 +++++++++++++++++++++++++-----------
 fluid/Fl_Type.cxx                   |   1 +
 fluid/Fl_Window_Type.cxx            |  31 ++++--
 fluid/Fluid_Image.cxx               |   5 +
 fluid/Fluid_Image.h                 |   1 +
 fluid/alignment_panel.cxx           |  42 ++++++--
 fluid/alignment_panel.fl            |  36 ++++---
 fluid/alignment_panel.h             |   2 +
 fluid/code.cxx                      |  45 +++++++--
 fluid/file.cxx                      |  19 ++--
 fluid/fluid.cxx                     |  41 +++++++-
 fluid/fluid.h                       |   2 +
 test/preferences.fl                 |  11 ++-
 15 files changed, 334 insertions(+), 114 deletions(-)

diff --git documentation/src/fluid-gettext.png documentation/src/fluid-gettext.png
index 8303062..f961c69 100644
Binary files documentation/src/fluid-gettext.png and documentation/src/fluid-gettext.png differ
diff --git documentation/src/fluid.dox documentation/src/fluid.dox
index fd4cf4a..b0682be 100644
--- documentation/src/fluid.dox
+++ documentation/src/fluid.dox
@@ -1503,8 +1503,10 @@ existing template, and click "Delete Template".
 \section fluid_i18n Internationalization with FLUID
 
 FLUID supports internationalization (I18N for short) of label
-strings used by widgets. The preferences window
-(<tt>Ctrl+p</tt>) provides access to the I18N options.
+strings and tooltips used by widgets. The GNU gettext option also
+supports deferred translation of statically initialised menu item
+labels. The preferences window (<tt>Ctrl+p</tt>) provides access
+to the I18N options.
 
 \subsection fluid_i18n_methods I18N Methods
 
@@ -1530,10 +1532,10 @@ need to call \p setlocale() and \p textdomain() or
 message file.
 
 To use GNU gettext for I18N, open the preferences window and
-choose "GNU gettext" from the \b Use: chooser. Two new input
+choose "GNU gettext" from the \b Use: chooser. Four new input
 fields will then appear to control the include file and
-function/macro name to use when retrieving the localized label
-strings.
+function/macro names to use when retrieving localized label
+strings in dynamic allocation and static initialisation.
 
  \image html fluid-gettext.png "Internationalization using GNU gettext"
  \image latex fluid-gettext.png "Internationalization using GNU gettext" width=10cm
@@ -1543,10 +1545,24 @@ field controls the header file to include for
 I18N; by default this is \b <libintl.h>, the
 standard I18N file for GNU gettext.
 
+If the \b Conditional: field contains a macro name, i18n will only be compiled
+into the product if this macro is defined. The build system should define the
+macro only if all required headers and libraries are available. If the macro
+is not defined, no headers are included and `gettext` passes text through
+untranslated.
+
 The \b Function: field controls the function (or macro) that
 will retrieve the localized message; by default the
 \p gettext function will be called.
 
+The **Static Function:** field names a macro that will mark static text
+fields for extraction with the `xgettext` tool. The default macro name is
+\p gettext_noop and will be defined as `#define gettext_noop(text) text`
+right after the `#include` statement. Fluid will do its best to call
+`gettext` on static texts after the textdomain was set by the user.
+
+\see [GNU gettext special cases](https://www.gnu.org/software/gettext/manual/html_node/Special-cases.html)
+
 \subsection fluid_catgets_i18n Using POSIX catgets for I18N
 
 FLUID's code support for POSIX catgets allows you to use a
diff --git fluid/Fl_Menu_Type.cxx fluid/Fl_Menu_Type.cxx
index d7e5fc5..e520087 100644
--- fluid/Fl_Menu_Type.cxx
+++ fluid/Fl_Menu_Type.cxx
@@ -39,6 +39,7 @@
 #include <FL/Fl_Menu_Button.H>
 #include <FL/Fl_Output.H>
 #include <FL/fl_draw.H>
+#include <FL/Fl_Multi_Label.H>
 #include "../src/flstring.h"
 
 #include <stdio.h>
@@ -53,6 +54,32 @@ Fl_Menu_Item menu_item_type_menu[] = {
 static char submenuflag;
 static uchar menuitemtype = 0;
 
+static void delete_dependents(Fl_Menu_Item *m) {
+  if (!m)
+    return;
+  int level = 0;
+  for (;;m++) {
+    if (m->label()==NULL) {
+      if (level==0) {
+        break;
+      } else {
+        level--;
+      }
+    }
+    if (m->flags&FL_SUBMENU)
+      level++;
+    if (m->labeltype()==FL_MULTI_LABEL)
+      delete (Fl_Multi_Label*)m->label();
+  }
+}
+
+static void delete_menu(Fl_Menu_Item *m) {
+  if (!m)
+    return;
+  delete_dependents(m);
+  delete[] m;
+}
+
 void Fl_Input_Choice_Type::build_menu() {
   Fl_Input_Choice* w = (Fl_Input_Choice*)o;
   // count how many Fl_Menu_Item structures needed:
@@ -63,23 +90,35 @@ void Fl_Input_Choice_Type::build_menu() {
     n++;
   }
   if (!n) {
-    if (menusize) delete[] (Fl_Menu_Item*)(w->menu());
+    if (menusize) delete_menu((Fl_Menu_Item*)(w->menu()));
     w->menu(0);
     menusize = 0;
   } else {
     n++; // space for null at end of menu
     if (menusize<n) {
-      if (menusize) delete[] (Fl_Menu_Item*)(w->menu());
+      if (menusize) delete_menu((Fl_Menu_Item*)(w->menu()));
       menusize = n+10;
       w->menu(new Fl_Menu_Item[menusize]);
+    } else {
+      if (menusize) delete_dependents((Fl_Menu_Item*)(w->menu()));
     }
     // fill them all in:
     Fl_Menu_Item* m = (Fl_Menu_Item*)(w->menu());
     int lvl = level+1;
     for (q = next; q && q->level > level; q = q->next) {
       Fl_Menu_Item_Type* i = (Fl_Menu_Item_Type*)q;
-      if (i->o->image()) i->o->image()->label(m);
-      else {
+      if (i->o->image()) {
+        if (i->o->label() && i->o->label()[0]) {
+          Fl_Multi_Label *ml = new Fl_Multi_Label;
+          ml->labela = (char*)i->o->image();
+          ml->labelb = i->o->label();
+          ml->typea = FL_IMAGE_LABEL;
+          ml->typeb = FL_NORMAL_LABEL;
+          ml->label(m);
+        } else {
+          i->o->image()->label(m);
+        }
+      } else {
         m->label(i->o->label() ? i->o->label() : "(nolabel)");
         m->labeltype(i->o->labeltype());
       }
@@ -198,6 +237,8 @@ const char* Fl_Menu_Item_Type::menu_name(int& i) {
 }
 
 void Fl_Menu_Item_Type::write_static() {
+  if (image && label() && label()[0])
+    write_declare("#include <FL/Fl_Multi_Label.H>");
   if (callback() && is_name(callback()) && !user_defined(callback()))
     write_declare("extern void %s(Fl_Menu_*, %s);", callback(),
                   user_data_type() ? user_data_type() : "void*");
@@ -265,11 +306,9 @@ void Fl_Menu_Item_Type::write_static() {
   const char* k = class_name(1);
   if (k) {
     int i;
-    if (i18n_type) write_c("\nunsigned char %s::%s_i18n_done = 0;", k, menu_name(i));
     write_c("\nFl_Menu_Item %s::%s[] = {\n", k, menu_name(i));
   } else {
     int i;
-    if (i18n_type) write_c("\nunsigned char %s_i18n_done = 0;", menu_name(i));
     write_c("\nFl_Menu_Item %s[] = {\n", menu_name(i));
   }
   Fl_Type* t = prev; while (t && t->is_menu_item()) t = t->prev;
@@ -331,9 +370,21 @@ void Fl_Menu_Item_Type::write_item() {
 
   write_comment_inline_c(" ");
   write_c(" {");
-  if (image) write_c("0");
-  else if (label()) write_cstring(label()); // we will call i18n when the widget is instantiated for the first time
-  else write_c("\"\"");
+  if (label() && label()[0])
+    switch (i18n_type) {
+      case 1:
+        // we will call i18n when the menu is instantiated for the first time
+        write_c("%s(", i18n_static_function);
+        write_cstring(label());
+        write_c(")");
+        break;
+      case 2:
+        // fall through: strings can't be translated before a catalog is choosen
+      default:
+        write_cstring(label());
+    }
+  else
+    write_c("\"\"");
   if (((Fl_Button*)o)->shortcut()) {
                 int s = ((Fl_Button*)o)->shortcut();
                 if (use_FL_COMMAND && (s & (FL_CTRL|FL_META))) {
@@ -361,16 +412,22 @@ void Fl_Menu_Item_Type::write_item() {
   write_c("},\n");
 }
 
+void start_menu_initialiser(int &initialized, const char *name, int index) {
+  if (!initialized) {
+    initialized = 1;
+    write_c("%s{ Fl_Menu_Item* o = &%s[%d];\n", indent(), name, index);
+    indentation++;
+  }
+}
+
 void Fl_Menu_Item_Type::write_code1() {
   int i; const char* mname = menu_name(i);
 
   if (!prev->is_menu_item()) {
     // for first menu item, declare the array
     if (class_name(1)) {
-      if (i18n_type) write_h("%sstatic unsigned char %s_i18n_done;\n", indent(1), mname);
       write_h("%sstatic Fl_Menu_Item %s[];\n", indent(1), mname);
     } else {
-      if (i18n_type) write_h("extern unsigned char %s_i18n_done;\n", mname);
       write_h("extern Fl_Menu_Item %s[];\n", mname);
     }
   }
@@ -401,24 +458,59 @@ void Fl_Menu_Item_Type::write_code1() {
   int menuItemInitialized = 0;
   // if the name is an array variable, assign the value here
   if (name() && strchr(name(), '[')) {
-    write_c("%s%s = &%s[%d];\n", indent(), name(), mname, i);
+    write_c("%s%s = &%s[%d];\n", indent_plus(1), name(), mname, i);
   }
   if (image) {
-    menuItemInitialized = 1;
-    write_c("%s{ Fl_Menu_Item* o = &%s[%d];\n", indent(), mname, i);
-    image->write_code("o");
+    start_menu_initialiser(menuItemInitialized, mname, i);
+    if (label() && label()[0]) {
+      write_c("%sFl_Multi_Label *ml = new Fl_Multi_Label;\n", indent());
+      write_c("%sml->labela = (char*)", indent());
+      image->write_inline();
+      write_c(";\n");
+      if (i18n_type==0) {
+        write_c("%sml->labelb = o->label();\n", indent());
+      } else if (i18n_type==1) {
+        write_c("%sml->labelb = %s(o->label());\n",
+                indent(), i18n_function);
+      } else if (i18n_type==2) {
+        write_c("%sml->labelb = catgets(%s,%s,i+%d,o->label());\n",
+                indent(), i18n_file[0] ? i18n_file : "_catalog",
+                i18n_set, msgnum());
+      }
+      write_c("%sml->typea = FL_IMAGE_LABEL;\n", indent());
+      write_c("%sml->typeb = FL_NORMAL_LABEL;\n", indent());
+      write_c("%sml->label(o);\n", indent());
+    } else {
+      image->write_code("o");
+    }
+  }
+  if (i18n_type && label() && label()[0]) {
+    Fl_Labeltype t = o->labeltype();
+    if (image) {
+      // label was already copied a few lines up
+    } else if (   t==FL_NORMAL_LABEL   || t==FL_SHADOW_LABEL
+               || t==FL_ENGRAVED_LABEL || t==FL_EMBOSSED_LABEL) {
+      start_menu_initialiser(menuItemInitialized, mname, i);
+      if (i18n_type==1) {
+        write_c("%so->label(%s(o->label()));\n",
+                indent(), i18n_function);
+      } else if (i18n_type==2) {
+        write_c("%so->label(catgets(%s,%s,i+%d,o->label()));\n",
+                indent(), i18n_file[0] ? i18n_file : "_catalog",
+                i18n_set, msgnum());
+      }
+    }
   }
   for (int n=0; n < NUM_EXTRA_CODE; n++) {
     if (extra_code(n) && !isdeclare(extra_code(n))) {
-      if (!menuItemInitialized) {
-        menuItemInitialized = 1;
-        write_c("%s{ Fl_Menu_Item* o = &%s[%d];\n", indent(), mname, i);
-      }
-      write_c("%s%s\n", indent_plus(1), extra_code(n));
+      start_menu_initialiser(menuItemInitialized, mname, i);
+      write_c("%s%s\n", indent(), extra_code(n));
     }
   }
-  if (menuItemInitialized)
+  if (menuItemInitialized) {
+    indentation--;
     write_c("%s}\n",indent());
+  }
 }
 
 void Fl_Menu_Item_Type::write_code2() {}
@@ -440,23 +532,35 @@ void Fl_Menu_Type::build_menu() {
     n++;
   }
   if (!n) {
-    if (menusize) delete[] (Fl_Menu_Item*)(w->menu());
+    if (menusize) delete_menu((Fl_Menu_Item*)(w->menu()));
     w->menu(0);
     menusize = 0;
   } else {
     n++; // space for null at end of menu
     if (menusize<n) {
-      if (menusize) delete[] (Fl_Menu_Item*)(w->menu());
+      if (menusize) delete_menu((Fl_Menu_Item*)(w->menu()));
       menusize = n+10;
       w->menu(new Fl_Menu_Item[menusize]);
+    } else {
+      if (menusize) delete_dependents((Fl_Menu_Item*)(w->menu()));
     }
     // fill them all in:
     Fl_Menu_Item* m = (Fl_Menu_Item*)(w->menu());
     int lvl = level+1;
     for (q = next; q && q->level > level; q = q->next) {
       Fl_Menu_Item_Type* i = (Fl_Menu_Item_Type*)q;
-      if (i->o->image()) i->o->image()->label(m);
-      else {
+      if (i->o->image()) {
+        if (i->o->label() && i->o->label()[0]) {
+          Fl_Multi_Label *ml = new Fl_Multi_Label;
+          ml->labela = (char*)i->o->image();
+          ml->labelb = i->o->label();
+          ml->typea = FL_IMAGE_LABEL;
+          ml->typeb = FL_NORMAL_LABEL;
+          ml->label(m);
+        } else {
+          i->o->image()->label(m);
+        }
+      } else {
         m->label(i->o->label() ? i->o->label() : "(nolabel)");
         m->labeltype(i->o->labeltype());
       }
@@ -498,38 +602,6 @@ Fl_Type* Fl_Menu_Type::click_test(int, int) {
 
 void Fl_Menu_Type::write_code2() {
   if (next && next->is_menu_item()) {
-    if (i18n_type) {
-      // take care of i18n now!
-      Fl_Menu_Item_Type *mi = (Fl_Menu_Item_Type*)next;
-      int i, nItem = 0, nLabel = 0;
-      const char *mName = mi->menu_name(i);
-      for (Fl_Type* q = next; q && q->is_menu_item(); q = q->next) {
-        if (((Fl_Menu_Item_Type*)q)->label()) nLabel++;
-        int thislevel = q->level; if (q->is_parent()) thislevel++;
-        int nextlevel =
-            (q->next && q->next->is_menu_item()) ? q->next->level : next->level+1;
-        nItem += 1 + ((thislevel > nextlevel) ? (thislevel-nextlevel) : 0);
-      }
-      if (nLabel) {
-        write_c("%sif (!%s_i18n_done) {\n", indent(), mName);
-        write_c("%sint i=0;\n", indent_plus(1));
-        write_c("%sfor ( ; i<%d; i++)\n", indent_plus(1), nItem);
-        write_c("%sif (%s[i].label())\n", indent_plus(2), mName);
-        switch (i18n_type) {
-          case 1:
-            write_c("%s%s[i].label(%s(%s[i].label()));\n",
-                    indent_plus(3), mName, i18n_function, mName);
-            break;
-          case 2:
-            write_c("%s%s[i].label(catgets(%s,%s,i+%d,%s[i].label()));\n",
-                    indent_plus(3), mName, i18n_file[0] ? i18n_file : "_catalog",
-                    i18n_set, mi->msgnum(), mName);
-            break;
-        }
-        write_c("%s%s_i18n_done = 1;\n", indent_plus(1), mName);
-        write_c("%s}\n", indent());
-      }
-    }
     write_c("%s%s->menu(%s);\n", indent(), name() ? name() : "o",
             unique_id(this, "menu", name(), label()));
   }
diff --git fluid/Fl_Type.cxx fluid/Fl_Type.cxx
index 7b20f53..2669cb7 100644
--- fluid/Fl_Type.cxx
+++ fluid/Fl_Type.cxx
@@ -179,6 +179,7 @@ void delete_all(int selected_only) {
     } else f = f->next;
   }
   if(!selected_only) {
+    // FIXME: undo/redo uses this function, resetting the following preferences randomly
     include_H_from_C=1;
     use_FL_COMMAND=0;
     utf8_in_src = 0;
diff --git fluid/Fl_Window_Type.cxx fluid/Fl_Window_Type.cxx
index 81ac5fb..ddc94fc 100644
--- fluid/Fl_Window_Type.cxx
+++ fluid/Fl_Window_Type.cxx
@@ -45,11 +45,6 @@
 int include_H_from_C = 1;
 int use_FL_COMMAND = 0;
 int utf8_in_src = 0;
-extern int i18n_type;
-extern const char* i18n_include;
-extern const char* i18n_function;
-extern const char* i18n_file;
-extern const char* i18n_set;
 
 extern Fl_Preferences   fluid_prefs;
 
@@ -125,31 +120,43 @@ void i18n_type_cb(Fl_Choice *c, void *) {
   switch (i18n_type = c->value()) {
   case 0 : /* None */
       i18n_include_input->hide();
+      i18n_conditional_input->hide();
       i18n_file_input->hide();
       i18n_set_input->hide();
       i18n_function_input->hide();
+      i18n_static_function_input->hide();
       break;
   case 1 : /* GNU gettext */
       i18n_include_input->value("<libintl.h>");
       i18n_include = i18n_include_input->value();
+      i18n_conditional_input->value("");
+      i18n_conditional = i18n_conditional_input->value();
       i18n_function_input->value("gettext");
       i18n_function = i18n_function_input->value();
+      i18n_static_function_input->value("gettext_noop");
+      i18n_static_function = i18n_static_function_input->value();
       i18n_include_input->show();
+      i18n_conditional_input->show();
       i18n_file_input->hide();
       i18n_set_input->hide();
       i18n_function_input->show();
+      i18n_static_function_input->show();
       break;
   case 2 : /* POSIX cat */
       i18n_include_input->value("<nl_types.h>");
+      i18n_include = i18n_include_input->value();
+      i18n_conditional_input->value("");
+      i18n_conditional = i18n_conditional_input->value();
       i18n_file_input->value("");
       i18n_file = i18n_file_input->value();
       i18n_set_input->value("1");
       i18n_set = i18n_set_input->value();
       i18n_include_input->show();
-      i18n_include = i18n_include_input->value();
+      i18n_conditional_input->show();
       i18n_file_input->show();
       i18n_set_input->show();
       i18n_function_input->hide();
+      i18n_static_function_input->hide();
       break;
   }
 
@@ -161,10 +168,14 @@ void i18n_text_cb(Fl_Input *i, void *) {
 
   if (i == i18n_function_input)
     i18n_function = i->value();
+  else if (i == i18n_static_function_input)
+    i18n_static_function = i->value();
   else if (i == i18n_file_input)
     i18n_file = i->value();
   else if (i == i18n_include_input)
     i18n_include = i->value();
+  else if (i == i18n_conditional_input)
+    i18n_conditional = i->value();
 
   set_modflag(1);
 }
@@ -187,27 +198,35 @@ void show_project_cb(Fl_Widget *, void *) {
   code_file_input->value(code_file_name);
   i18n_type_chooser->value(i18n_type);
   i18n_function_input->value(i18n_function);
+  i18n_static_function_input->value(i18n_static_function);
   i18n_file_input->value(i18n_file);
   i18n_set_input->value(i18n_set);
   i18n_include_input->value(i18n_include);
+  i18n_conditional_input->value(i18n_conditional);
   switch (i18n_type) {
   case 0 : /* None */
       i18n_include_input->hide();
+      i18n_conditional_input->hide();
       i18n_file_input->hide();
       i18n_set_input->hide();
       i18n_function_input->hide();
+      i18n_static_function_input->hide();
       break;
   case 1 : /* GNU gettext */
       i18n_include_input->show();
+      i18n_conditional_input->show();
       i18n_file_input->hide();
       i18n_set_input->hide();
       i18n_function_input->show();
+      i18n_static_function_input->show();
       break;
   case 2 : /* POSIX cat */
       i18n_include_input->show();
+      i18n_conditional_input->show();
       i18n_file_input->show();
       i18n_set_input->show();
       i18n_function_input->hide();
+      i18n_static_function_input->hide();
       break;
   }
   project_window->hotspot(project_window);
diff --git fluid/Fluid_Image.cxx fluid/Fluid_Image.cxx
index 6576763..a63355a 100644
--- fluid/Fluid_Image.cxx
+++ fluid/Fluid_Image.cxx
@@ -157,6 +157,11 @@ void Fluid_Image::write_code(const char *var, int inactive) {
   if (img) write_c("%s%s->%s( %s() );\n", indent(), var, inactive ? "deimage" : "image", function_name_);
 }
 
+void Fluid_Image::write_inline(int inactive) {
+  if (img)
+    write_c("%s()", function_name_);
+}
+
 
 ////////////////////////////////////////////////////////////////
 
diff --git fluid/Fluid_Image.h fluid/Fluid_Image.h
index 99b06d2..d11e248 100644
--- fluid/Fluid_Image.h
+++ fluid/Fluid_Image.h
@@ -41,6 +41,7 @@ public:
   void write_static();
   void write_initializer(const char *type_name, const char *format, ...);
   void write_code(const char *var, int inactive = 0);
+  void write_inline(int inactive = 0);
   const char *name() const {return name_;}
 };
 
diff --git fluid/alignment_panel.cxx fluid/alignment_panel.cxx
index 532cf10..3a76c40 100644
--- fluid/alignment_panel.cxx
+++ fluid/alignment_panel.cxx
@@ -49,21 +49,26 @@ Fl_Menu_Item menu_i18n_type_chooser[] = {
 
 Fl_Input *i18n_include_input=(Fl_Input *)0;
 
+Fl_Input *i18n_conditional_input=(Fl_Input *)0;
+
 Fl_Input *i18n_file_input=(Fl_Input *)0;
 
 Fl_Int_Input *i18n_set_input=(Fl_Int_Input *)0;
 
 Fl_Input *i18n_function_input=(Fl_Input *)0;
 
+Fl_Input *i18n_static_function_input=(Fl_Input *)0;
+
 Fl_Double_Window* make_project_window() {
   { project_window = new Fl_Double_Window(399, 275, "Project Settings");
     { Fl_Button* o = new Fl_Button(328, 239, 60, 25, "Close");
       o->tooltip("Close this dialog.");
       o->callback((Fl_Callback*)cb_Close);
     } // Fl_Button* o
-    { Fl_Tabs* o = new Fl_Tabs(10, 10, 378, 218);
+    { Fl_Tabs* o = new Fl_Tabs(10, 10, 379, 218);
       o->selection_color((Fl_Color)12);
-      { Fl_Group* o = new Fl_Group(10, 36, 378, 192, "Output");
+      o->labelcolor(FL_BACKGROUND2_COLOR);
+      { Fl_Group* o = new Fl_Group(10, 36, 379, 192, "Output");
         { Fl_Box* o = new Fl_Box(20, 49, 340, 49, "Use \"name.ext\" to set a file name or just \".ext\" to set extension.");
           o->align(Fl_Align(132|FL_ALIGN_INSIDE));
         } // Fl_Box* o
@@ -89,7 +94,7 @@ Fl_Double_Window* make_project_window() {
           include_H_from_C_button->callback((Fl_Callback*)include_H_from_C_button_cb);
         } // Fl_Check_Button* include_H_from_C_button
         { use_FL_COMMAND_button = new Fl_Check_Button(117, 176, 272, 20, "Menu shortcuts use FL_COMMAND");
-          use_FL_COMMAND_button->tooltip("Replace FL_CTRL with FL_COMMAND when generating menu shortcut code.");
+          use_FL_COMMAND_button->tooltip("Replace FL_CTRL and FL_META with FL_COMMAND when generating menu shortcuts");
           use_FL_COMMAND_button->down_box(FL_DOWN_BOX);
           use_FL_COMMAND_button->callback((Fl_Callback*)use_FL_COMMAND_button_cb);
         } // Fl_Check_Button* use_FL_COMMAND_button
@@ -104,7 +109,7 @@ ped using octal notation `\\0123`. If this option is checked, Fluid will write\
       } // Fl_Group* o
       { Fl_Group* o = new Fl_Group(10, 36, 378, 192, "Internationalization");
         o->hide();
-        { i18n_type_chooser = new Fl_Choice(100, 48, 136, 25, "Use:");
+        { i18n_type_chooser = new Fl_Choice(128, 48, 136, 25, "Use:");
           i18n_type_chooser->tooltip("Type of internationalization to use.");
           i18n_type_chooser->box(FL_THIN_UP_BOX);
           i18n_type_chooser->down_box(FL_BORDER_BOX);
@@ -112,21 +117,29 @@ ped using octal notation `\\0123`. If this option is checked, Fluid will write\
           i18n_type_chooser->callback((Fl_Callback*)i18n_type_cb);
           i18n_type_chooser->menu(menu_i18n_type_chooser);
         } // Fl_Choice* i18n_type_chooser
-        { i18n_include_input = new Fl_Input(100, 78, 272, 20, "#include:");
+        { i18n_include_input = new Fl_Input(128, 78, 243, 20, "#include:");
           i18n_include_input->tooltip("The include file for internationalization.");
           i18n_include_input->box(FL_THIN_DOWN_BOX);
           i18n_include_input->labelfont(1);
           i18n_include_input->textfont(4);
           i18n_include_input->callback((Fl_Callback*)i18n_text_cb);
         } // Fl_Input* i18n_include_input
-        { i18n_file_input = new Fl_Input(100, 104, 272, 20, "File:");
+        { i18n_conditional_input = new Fl_Input(128, 103, 243, 20, "Conditional:");
+          i18n_conditional_input->tooltip("only include the header file if this preprocessor macro is defined, for examp\
+le FLTK_GETTEXT_FOUND");
+          i18n_conditional_input->box(FL_THIN_DOWN_BOX);
+          i18n_conditional_input->labelfont(1);
+          i18n_conditional_input->textfont(4);
+          i18n_conditional_input->callback((Fl_Callback*)i18n_text_cb);
+        } // Fl_Input* i18n_conditional_input
+        { i18n_file_input = new Fl_Input(128, 128, 243, 20, "File:");
           i18n_file_input->tooltip("The name of the message catalog.");
           i18n_file_input->box(FL_THIN_DOWN_BOX);
           i18n_file_input->labelfont(1);
           i18n_file_input->textfont(4);
           i18n_file_input->callback((Fl_Callback*)i18n_text_cb);
         } // Fl_Input* i18n_file_input
-        { i18n_set_input = new Fl_Int_Input(100, 128, 272, 20, "Set:");
+        { i18n_set_input = new Fl_Int_Input(128, 153, 243, 20, "Set:");
           i18n_set_input->tooltip("The message set number.");
           i18n_set_input->type(2);
           i18n_set_input->box(FL_THIN_DOWN_BOX);
@@ -134,13 +147,22 @@ ped using octal notation `\\0123`. If this option is checked, Fluid will write\
           i18n_set_input->textfont(4);
           i18n_set_input->callback((Fl_Callback*)i18n_int_cb);
         } // Fl_Int_Input* i18n_set_input
-        { i18n_function_input = new Fl_Input(100, 103, 272, 20, "Function:");
-          i18n_function_input->tooltip("The function to call to internationalize the labels and tooltips.");
+        { i18n_function_input = new Fl_Input(128, 128, 243, 20, "Function:");
+          i18n_function_input->tooltip("The function to call to translate labels and tooltips, usually \"gettext\" or\
+ \"_\"");
           i18n_function_input->box(FL_THIN_DOWN_BOX);
           i18n_function_input->labelfont(1);
           i18n_function_input->textfont(4);
           i18n_function_input->callback((Fl_Callback*)i18n_text_cb);
         } // Fl_Input* i18n_function_input
+        { i18n_static_function_input = new Fl_Input(128, 153, 243, 20, "Static Function:");
+          i18n_static_function_input->tooltip("function to call to translate static text, The function to call to internatio\
+nalize labels and tooltips, usually \"gettext_noop\" or \"N_\"");
+          i18n_static_function_input->box(FL_THIN_DOWN_BOX);
+          i18n_static_function_input->labelfont(1);
+          i18n_static_function_input->textfont(4);
+          i18n_static_function_input->callback((Fl_Callback*)i18n_text_cb);
+        } // Fl_Input* i18n_static_function_input
         o->end();
       } // Fl_Group* o
       o->end();
@@ -525,7 +547,7 @@ Fl_Double_Window* make_layout_window() {
       o->labelfont(1);
       o->align(Fl_Align(FL_ALIGN_RIGHT|FL_ALIGN_INSIDE));
     } // Fl_Box* o
-    { Fl_Group* o = new Fl_Group(105, 115, 170, 75);
+    { Fl_Group* o = new Fl_Group(105, 115, 192, 75);
       { def_widget_size[0] = new Fl_Round_Button(115, 115, 70, 25);
         def_widget_size[0]->type(102);
         def_widget_size[0]->down_box(FL_ROUND_DOWN_BOX);
diff --git fluid/alignment_panel.fl fluid/alignment_panel.fl
index 0bce368..ca8fb6c 100644
--- fluid/alignment_panel.fl
+++ fluid/alignment_panel.fl
@@ -54,7 +54,7 @@ Function {make_project_window()} {open
 } {
   Fl_Window project_window {
     label {Project Settings} open
-    xywh {473 246 399 275} type Double
+    xywh {472 246 399 275} type Double
     code0 {\#include <FL/Fl_Preferences.H>}
     code1 {\#include <FL/Fl_Tooltip.H>} modal visible
   } {
@@ -65,11 +65,11 @@ set_modflag(-1, -1);}
       tooltip {Close this dialog.} xywh {328 239 60 25}
     }
     Fl_Tabs {} {open
-      xywh {10 10 378 218} selection_color 12
+      xywh {10 10 379 218} selection_color 12 labelcolor 7
     } {
       Fl_Group {} {
-        label Output open
-        xywh {10 36 378 192}
+        label Output open selected
+        xywh {10 36 379 192}
       } {
         Fl_Box {} {
           label {Use "name.ext" to set a file name or just ".ext" to set extension.}
@@ -95,11 +95,11 @@ set_modflag(-1, -1);}
         Fl_Check_Button use_FL_COMMAND_button {
           label {Menu shortcuts use FL_COMMAND}
           callback use_FL_COMMAND_button_cb
-          tooltip {Replace FL_CTRL with FL_COMMAND when generating menu shortcut code.} xywh {117 176 272 20} down_box DOWN_BOX
+          tooltip {Replace FL_CTRL and FL_META with FL_COMMAND when generating menu shortcuts} xywh {117 176 272 20} down_box DOWN_BOX
         }
         Fl_Check_Button utf8_in_src_button {
           label {allow Unicode UTF-8 in source code}
-          callback utf8_in_src_cb selected
+          callback utf8_in_src_cb
           tooltip {For older compilers, characters outside of the printable ASCII range are escaped using octal notation `\\0123`. If this option is checked, Fluid will write UTF-8 characters unchanged.} xywh {117 199 272 20} down_box DOWN_BOX
         }
       }
@@ -110,7 +110,7 @@ set_modflag(-1, -1);}
         Fl_Choice i18n_type_chooser {
           label {Use:}
           callback i18n_type_cb open
-          tooltip {Type of internationalization to use.} xywh {100 48 136 25} box THIN_UP_BOX down_box BORDER_BOX labelfont 1
+          tooltip {Type of internationalization to use.} xywh {128 48 136 25} box THIN_UP_BOX down_box BORDER_BOX labelfont 1
         } {
           MenuItem {} {
             label None
@@ -128,22 +128,32 @@ set_modflag(-1, -1);}
         Fl_Input i18n_include_input {
           label {\#include:}
           callback i18n_text_cb
-          tooltip {The include file for internationalization.} xywh {100 78 272 20} box THIN_DOWN_BOX labelfont 1 textfont 4
+          tooltip {The include file for internationalization.} xywh {128 78 243 20} box THIN_DOWN_BOX labelfont 1 textfont 4
+        }
+        Fl_Input i18n_conditional_input {
+          label {Conditional:}
+          callback i18n_text_cb
+          tooltip {only include the header file if this preprocessor macro is defined, for example FLTK_GETTEXT_FOUND} xywh {128 103 243 20} box THIN_DOWN_BOX labelfont 1 textfont 4
         }
         Fl_Input i18n_file_input {
           label {File:}
           callback i18n_text_cb
-          tooltip {The name of the message catalog.} xywh {100 104 272 20} box THIN_DOWN_BOX labelfont 1 textfont 4
+          tooltip {The name of the message catalog.} xywh {128 128 243 20} box THIN_DOWN_BOX labelfont 1 textfont 4
         }
         Fl_Input i18n_set_input {
           label {Set:}
           callback i18n_int_cb
-          tooltip {The message set number.} xywh {100 128 272 20} type Int box THIN_DOWN_BOX labelfont 1 textfont 4
+          tooltip {The message set number.} xywh {128 153 243 20} type Int box THIN_DOWN_BOX labelfont 1 textfont 4
         }
         Fl_Input i18n_function_input {
           label {Function:}
           callback i18n_text_cb
-          tooltip {The function to call to internationalize the labels and tooltips.} xywh {100 103 272 20} box THIN_DOWN_BOX labelfont 1 textfont 4
+          tooltip {The function to call to translate labels and tooltips, usually "gettext" or "_"} xywh {128 128 243 20} box THIN_DOWN_BOX labelfont 1 textfont 4
+        }
+        Fl_Input i18n_static_function_input {
+          label {Static Function:}
+          callback i18n_text_cb
+          tooltip {function to call to translate static text, The function to call to internationalize labels and tooltips, usually "gettext_noop" or "N_"} xywh {128 153 243 20} box THIN_DOWN_BOX labelfont 1 textfont 4
         }
       }
     }
@@ -388,7 +398,7 @@ Function {make_layout_window()} {open
 } {
   Fl_Window grid_window {
     label {Layout Settings}
-    xywh {885 274 310 245} type Double non_modal visible
+    xywh {745 303 310 245} type Double non_modal visible
   } {
     Fl_Input horizontal_input {
       label x
@@ -431,7 +441,7 @@ Function {make_layout_window()} {open
       xywh {10 115 107 25} labelfont 1 align 24
     }
     Fl_Group {} {open
-      xywh {105 115 170 75}
+      xywh {105 115 192 75}
     } {
       Fl_Round_Button {def_widget_size[0]} {
         user_data 8 user_data_type long
diff --git fluid/alignment_panel.h fluid/alignment_panel.h
index a7cc711..9a6e609 100644
--- fluid/alignment_panel.h
+++ fluid/alignment_panel.h
@@ -51,11 +51,13 @@ extern void i18n_type_cb(Fl_Choice*, void*);
 extern Fl_Choice *i18n_type_chooser;
 extern void i18n_text_cb(Fl_Input*, void*);
 extern Fl_Input *i18n_include_input;
+extern Fl_Input *i18n_conditional_input;
 extern Fl_Input *i18n_file_input;
 #include <FL/Fl_Int_Input.H>
 extern void i18n_int_cb(Fl_Int_Input*, void*);
 extern Fl_Int_Input *i18n_set_input;
 extern Fl_Input *i18n_function_input;
+extern Fl_Input *i18n_static_function_input;
 Fl_Double_Window* make_project_window();
 extern Fl_Menu_Item menu_i18n_type_chooser[];
 extern void i18n_cb(Fl_Choice *,void *);
diff --git fluid/code.cxx fluid/code.cxx
index e9af1fd..d6b4bdc 100644
--- fluid/code.cxx
+++ fluid/code.cxx
@@ -573,28 +573,55 @@ int write_code(const char *s, const char *t) {
   }
 
   write_declare("#include <FL/Fl.H>");
+  if (t && include_H_from_C) {
+    if (*header_file_name == '.' && strchr(header_file_name, '/') == NULL) {
+      write_c("#include \"%s\"\n", fl_filename_name(t));
+    } else {
+      write_c("#include \"%s\"\n", t);
+    }
+  }
   if (i18n_type && i18n_include[0]) {
+    int conditional = (i18n_conditional[0]!=0);
+    if (conditional) {
+      write_c("#ifdef %s\n", i18n_conditional);
+      indentation++;
+    }
     if (i18n_include[0] != '<' &&
         i18n_include[0] != '\"')
-      write_c("#include \"%s\"\n", i18n_include);
+      write_c("#%sinclude \"%s\"\n", indent(), i18n_include);
     else
-      write_c("#include %s\n", i18n_include);
+      write_c("#%sinclude %s\n", indent(), i18n_include);
     if (i18n_type == 2) {
       if (i18n_file[0]) write_c("extern nl_catd %s;\n", i18n_file);
       else {
         write_c("// Initialize I18N stuff now for menus...\n");
-        write_c("#include <locale.h>\n");
+        write_c("#%sinclude <locale.h>\n", indent());
         write_c("static char *_locale = setlocale(LC_MESSAGES, \"\");\n");
         write_c("static nl_catd _catalog = catopen(\"%s\", 0);\n",
                    i18n_program);
       }
     }
-  }
-  if (t && include_H_from_C) {
-    if (*header_file_name == '.' && strchr(header_file_name, '/') == NULL) {
-      write_c("#include \"%s\"\n", fl_filename_name(t));
-    } else {
-      write_c("#include \"%s\"\n", t);
+    if (conditional) {
+      write_c("#else\n");
+      if (i18n_type == 1) {
+        if (i18n_function[0]) {
+          write_c("#%sifndef %s\n", indent(), i18n_function);
+          write_c("#%sdefine %s(text) text\n", indent_plus(1), i18n_function);
+          write_c("#%sendif\n", indent());
+        }
+      }
+      if (i18n_type == 2) {
+        write_c("#%sifndef catgets\n", indent());
+        write_c("#%sdefine catgets(catalog, set, msgid, text) text\n", indent_plus(1));
+        write_c("#%sendif\n", indent());
+      }
+      indentation--;
+      write_c("#endif\n");
+    }
+    if (i18n_type == 1 && i18n_static_function[0]) {
+      write_c("#ifndef %s\n", i18n_static_function);
+      write_c("#%sdefine %s(text) text\n", indent_plus(1), i18n_static_function);
+      write_c("#endif\n");
     }
   }
   for (Fl_Type* p = first_type; p;) {
diff --git fluid/file.cxx fluid/file.cxx
index b38be1c..044e4b1 100644
--- fluid/file.cxx
+++ fluid/file.cxx
@@ -385,14 +385,16 @@ int write_file(const char *filename, int selected_only) {
     write_string("\nutf8_in_src");
   if (i18n_type) {
     write_string("\ni18n_type %d", i18n_type);
-    write_string("\ni18n_include %s", i18n_include);
+    write_string("\ni18n_include"); write_word(i18n_include);
+    write_string("\ni18n_conditional"); write_word(i18n_conditional);
     switch (i18n_type) {
     case 1 : /* GNU gettext */
-        write_string("\ni18n_function %s", i18n_function);
+        write_string("\ni18n_function"); write_word(i18n_function);
+        write_string("\ni18n_static_function"); write_word(i18n_static_function);
         break;
     case 2 : /* POSIX catgets */
-        if (i18n_file[0]) write_string("\ni18n_file %s", i18n_file);
-        write_string("\ni18n_set %s", i18n_set);
+        if (i18n_file[0]) write_string("\ni18n_file"); write_word(i18n_file);
+        write_string("\ni18n_set"); write_word(i18n_set);
         break;
     }
   }
@@ -507,6 +509,10 @@ static void read_children(Fl_Type *p, int paste, Strategy strategy, char skip_op
         i18n_function = fl_strdup(read_word());
         goto CONTINUE;
       }
+      if (!strcmp(c,"i18n_static_function")) {
+        i18n_static_function = fl_strdup(read_word());
+        goto CONTINUE;
+      }
       if (!strcmp(c,"i18n_file")) {
         i18n_file = fl_strdup(read_word());
         goto CONTINUE;
@@ -519,9 +525,8 @@ static void read_children(Fl_Type *p, int paste, Strategy strategy, char skip_op
         i18n_include = fl_strdup(read_word());
         goto CONTINUE;
       }
-      if (!strcmp(c,"i18n_type"))
-      {
-        i18n_type = atoi(read_word());
+      if (!strcmp(c,"i18n_conditional")) {
+        i18n_conditional = fl_strdup(read_word());
         goto CONTINUE;
       }
       if (!strcmp(c,"i18n_type"))
diff --git fluid/fluid.cxx fluid/fluid.cxx
index df8a713..95568d6 100644
--- fluid/fluid.cxx
+++ fluid/fluid.cxx
@@ -180,14 +180,47 @@ const char* code_file_name = ".cxx";
 /// \todo document me
 int i18n_type = 0;
 
-/// Saved in the .fl design file.
-/// \todo document me
+/**
+ For either type of translation, write a #include statement into the
+ source file.
+
+ This is usually `<libintl.h>` or `"gettext.h"` for GNU gettext, or
+ `<nl_types.h>` for Posix catgets.
+
+ Fluid accepts filenames in quotes or in \< and \>. If neither is found,
+ double quotes are added.
+ If this value is emty, no include statement will be generated.
+
+ Saved in the .fl design file.
+ */
 const char* i18n_include = "";
 
-/// Saved in the .fl design file.
-/// \todo document me
+const char* i18n_conditional = "";
+
+/**
+ For the gettext/intl.h options, this is the function that translates text
+ at runtime.
+
+ This is usually "gettext" or "_".
+ This should not be empty.
+ Saved in the .fl design file.
+
+ */
 const char* i18n_function = "";
 
+/**
+ For the gettext/intl.h options, this is the function that marks the
+ translation of text at initialisation time.
+
+ This is usually "gettext_noop" or "N_".
+ This should not be empty.
+ Fluid will translate static text (usually in menu items) later when used
+ for the first time.
+
+ Saved in the .fl design file.
+ */
+const char* i18n_static_function = "";
+
 /// Saved in the .fl design file.
 /// \todo document me
 const char* i18n_file = "";
diff --git fluid/fluid.h fluid/fluid.h
index ff30984..a00a775 100644
--- fluid/fluid.h
+++ fluid/fluid.h
@@ -82,7 +82,9 @@ extern const char* code_file_name;
 
 extern int i18n_type;
 extern const char* i18n_include;
+extern const char* i18n_conditional;
 extern const char* i18n_function;
+extern const char* i18n_static_function;
 extern const char* i18n_file;
 extern const char* i18n_set;;
 extern char i18n_program[FL_PATH_MAX];
diff --git test/preferences.fl test/preferences.fl
index b28e0c0..d90119f 100644
--- test/preferences.fl
+++ test/preferences.fl
@@ -1,5 +1,10 @@
 # data file for the Fltk User Interface Designer (fluid)
 version 1.0400
+i18n_type 1
+i18n_include {<libintl.h>}
+i18n_conditional FLTK_GETTEXT_FOUND
+i18n_function gettext
+i18n_static_function gettext_noop
 header_name {.h}
 code_name {.cxx}
 decl {\#include <FL/Fl_Preferences.H>} {public local
@@ -45,7 +50,7 @@ Function {} {open return_type int
   Fl_Window myWindow {
     label {My Preferences}
     callback closeWindowCB open
-    xywh {586 277 298 311} type Double visible
+    xywh {585 277 298 311} type Double visible
   } {
     Fl_Button {} {
       label Cancel
@@ -62,7 +67,7 @@ Function {} {open return_type int
       xywh {20 30 115 225} box ENGRAVED_FRAME align 5
     } {
       Fl_Input wAlarm {
-        label {Alarm at:} selected
+        label {Alarm at:}
         xywh {25 55 45 20} align 5
       }
       Fl_Choice wAmPm {open
@@ -73,7 +78,7 @@ Function {} {open return_type int
           xywh {0 0 100 20}
         }
         MenuItem {} {
-          label {p.m.}
+          label {p.m.} selected
           xywh {0 0 100 20}
         }
       }
Direct Link to Message ]
 
     
Previous Message ]Next Message ]
 
 

Comments are owned by the poster. All other content is copyright 1998-2024 by Bill Spitzak and others. This project is hosted by The FLTK Team. Please report site problems to 'erco@seriss.com'.