diff options
| author | Matthias Melcher <git@matthiasm.com> | 2021-12-19 01:09:13 +0100 |
|---|---|---|
| committer | Matthias Melcher <github@matthiasm.com> | 2021-12-19 02:03:05 +0100 |
| commit | 29531873ea4395a2dc65cefcc28db09569c38c18 (patch) | |
| tree | 97ae97c7b7f798c11fd291a0e2cc6bfb27e32b8a | |
| parent | 5438954d8c6213f1b0cc9d23b70ffaae388e1d60 (diff) | |
STR 3289: Fluid i18n, gettext, catguts improvements
Removed some unneeded code.
| -rw-r--r-- | documentation/src/fluid-gettext.png | bin | 4369 -> 32267 bytes | |||
| -rw-r--r-- | documentation/src/fluid.dox | 26 | ||||
| -rw-r--r-- | fluid/Fl_Menu_Type.cxx | 186 | ||||
| -rw-r--r-- | fluid/Fl_Type.cxx | 1 | ||||
| -rw-r--r-- | fluid/Fl_Window_Type.cxx | 31 | ||||
| -rw-r--r-- | fluid/Fluid_Image.cxx | 5 | ||||
| -rw-r--r-- | fluid/Fluid_Image.h | 1 | ||||
| -rw-r--r-- | fluid/alignment_panel.cxx | 42 | ||||
| -rw-r--r-- | fluid/alignment_panel.fl | 36 | ||||
| -rw-r--r-- | fluid/alignment_panel.h | 2 | ||||
| -rw-r--r-- | fluid/code.cxx | 45 | ||||
| -rw-r--r-- | fluid/file.cxx | 19 | ||||
| -rw-r--r-- | fluid/fluid.cxx | 41 | ||||
| -rw-r--r-- | fluid/fluid.h | 2 | ||||
| -rw-r--r-- | test/preferences.fl | 11 |
15 files changed, 334 insertions, 114 deletions
diff --git a/documentation/src/fluid-gettext.png b/documentation/src/fluid-gettext.png Binary files differindex 83030626b..f961c6929 100644 --- a/documentation/src/fluid-gettext.png +++ b/documentation/src/fluid-gettext.png diff --git a/documentation/src/fluid.dox b/documentation/src/fluid.dox index fd4cf4a38..b0682be9b 100644 --- a/documentation/src/fluid.dox +++ b/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 a/fluid/Fl_Menu_Type.cxx b/fluid/Fl_Menu_Type.cxx index d7e5fc5ea..e52008723 100644 --- a/fluid/Fl_Menu_Type.cxx +++ b/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 a/fluid/Fl_Type.cxx b/fluid/Fl_Type.cxx index 7b20f5312..2669cb7b1 100644 --- a/fluid/Fl_Type.cxx +++ b/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 a/fluid/Fl_Window_Type.cxx b/fluid/Fl_Window_Type.cxx index 81ac5fbb8..ddc94fc9c 100644 --- a/fluid/Fl_Window_Type.cxx +++ b/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 a/fluid/Fluid_Image.cxx b/fluid/Fluid_Image.cxx index 65767632b..a63355a32 100644 --- a/fluid/Fluid_Image.cxx +++ b/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 a/fluid/Fluid_Image.h b/fluid/Fluid_Image.h index 99b06d218..d11e2487b 100644 --- a/fluid/Fluid_Image.h +++ b/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 a/fluid/alignment_panel.cxx b/fluid/alignment_panel.cxx index 532cf10e0..3a76c4012 100644 --- a/fluid/alignment_panel.cxx +++ b/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 a/fluid/alignment_panel.fl b/fluid/alignment_panel.fl index 0bce36880..ca8fb6cbb 100644 --- a/fluid/alignment_panel.fl +++ b/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 a/fluid/alignment_panel.h b/fluid/alignment_panel.h index a7cc71102..9a6e6092a 100644 --- a/fluid/alignment_panel.h +++ b/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 a/fluid/code.cxx b/fluid/code.cxx index e9af1fd53..d6b4bdc38 100644 --- a/fluid/code.cxx +++ b/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 a/fluid/file.cxx b/fluid/file.cxx index b38be1cd6..044e4b156 100644 --- a/fluid/file.cxx +++ b/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 a/fluid/fluid.cxx b/fluid/fluid.cxx index df8a713a2..95568d63f 100644 --- a/fluid/fluid.cxx +++ b/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 a/fluid/fluid.h b/fluid/fluid.h index ff309843a..a00a775d3 100644 --- a/fluid/fluid.h +++ b/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 a/test/preferences.fl b/test/preferences.fl index b28e0c0ab..d90119ff0 100644 --- a/test/preferences.fl +++ b/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} } } |
