[gimp] libgimp: add a new set_i18n() method to GimpPlugIn class.
- From: Jehan <jehanp src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp] libgimp: add a new set_i18n() method to GimpPlugIn class.
- Date: Sun, 5 Jun 2022 00:00:08 +0000 (UTC)
commit 4b9d8a56cb8aa08913b8eb4848a3b18405073a6b
Author: Jehan <jehan girinstud io>
Date: Thu May 26 00:39:32 2022 +0200
libgimp: add a new set_i18n() method to GimpPlugIn class.
Clearly the old logic for localizing plug-ins was "core plug-ins first":
* In particular, we were defaulting to the "gimp*-std-plugins" domain,
asking plug-ins to override it with libgimp function
gimp_plug_in_set_translation_domain(). Obviously any third-party
plug-in would have to call this function since their strings are most
likely not the same as GIMP core plug-ins.
* Moreover this was only for the localization of menu items, which means
we had to duplicate work anyway (simplified for core C plug-ins only
with the INIT_I18N macro).
* Also plug-ins had to manage the location of the Gettext catalogs,
which is simpler for core plug-ins with gimp_locale_directory(), but
more annoying for third-party plug-ins which can be installed
basically anywhere (assuming their message catalogs are in their
directory, not centrally installed on the system, especially for
non-UNIX-like packages, with relocatable GIMP, no central package
system and so on).
* Finally in this logic of centrally installed catalogs, we were
requesting that the domain name is unique, which makes sense in a
Linux world with well maintained packages to avoid name clashes, not
in a world with people making plug-ins without knowing too much what's
done by their neighbour.
So now the new logic is:
- By default, GIMP will search for a folder called locale/ on the same
level as the plug-in executable. The Gettext domain will be the name
of the executable folder (and it doesn't matter too much if it's not
unique in such configuration).
- This can be disabled by overriding set_i18n() to NULL or
reimplementing it. All our core plug-ins will do this since they will
continue to use the centrally installed "gimp*-std-plugins" domain (or
"gimp*-python" for Python plug-ins).
- When not disabled while the folder is not available, warning messages
will be outputted to stderr, so that plug-in developers can easily
detect what is missing and how to handle it (and how to easily support
internationalization of their plug-in).
- gimp_plug_in_set_translation_domain() is removed and a future commit
is going to get rid of _gimp_plug_in_domain_register() because we are
going to get rid of the core-side localization of menu items (with
logic explained in the upcoming commit).
libgimp/gimp.def | 1 -
libgimp/gimpplugin-private.h | 5 +
libgimp/gimpplugin.c | 225 ++++++++++++++++++++++++++++++++++---------
libgimp/gimpplugin.h | 54 ++++++++++-
4 files changed, 238 insertions(+), 47 deletions(-)
---
diff --git a/libgimp/gimp.def b/libgimp/gimp.def
index bab2fe2e4b..b05044bb11 100644
--- a/libgimp/gimp.def
+++ b/libgimp/gimp.def
@@ -704,7 +704,6 @@ EXPORTS
gimp_plug_in_remove_temp_procedure
gimp_plug_in_set_help_domain
gimp_plug_in_set_pdb_error_handler
- gimp_plug_in_set_translation_domain
gimp_procedure_add_argument
gimp_procedure_add_argument_from_property
gimp_procedure_add_aux_argument
diff --git a/libgimp/gimpplugin-private.h b/libgimp/gimpplugin-private.h
index 9c45fe7f81..cda2238501 100644
--- a/libgimp/gimpplugin-private.h
+++ b/libgimp/gimpplugin-private.h
@@ -37,6 +37,11 @@ void _gimp_plug_in_read_expect_msg (GimpPlugIn *plug_in,
GimpWireMessage *msg,
gint type);
+gboolean _gimp_plug_in_set_i18n (GimpPlugIn *plug_in,
+ const gchar *procedure_name,
+ gchar **gettext_domain,
+ gchar **catalog_dir);
+
GimpProcedure * _gimp_plug_in_create_procedure (GimpPlugIn *plug_in,
const gchar *procedure_name);
diff --git a/libgimp/gimpplugin.c b/libgimp/gimpplugin.c
index 7e3db21358..35e89f14bc 100644
--- a/libgimp/gimpplugin.c
+++ b/libgimp/gimpplugin.c
@@ -22,6 +22,7 @@
#include "config.h"
#include <errno.h>
+#include <libintl.h>
#include <string.h>
#include "gimp.h"
@@ -166,6 +167,11 @@ static void gimp_plug_in_get_property (GObject *object,
GValue *value,
GParamSpec *pspec);
+static gboolean gimp_plug_in_real_set_i18n (GimpPlugIn *plug_in,
+ const gchar *procedure_name,
+ gchar **gettext_domain,
+ gchar **catalog_dir);
+
static void gimp_plug_in_register (GimpPlugIn *plug_in,
GList *procedures);
@@ -203,6 +209,8 @@ static void gimp_plug_in_destroy_hashes (GimpPlugIn *plug_in);
static void gimp_plug_in_destroy_proxies (GHashTable *hash_table,
gboolean destroy_all);
+static void gimp_plug_in_init_i18n (GimpPlugIn *plug_in);
+
G_DEFINE_TYPE_WITH_PRIVATE (GimpPlugIn, gimp_plug_in, G_TYPE_OBJECT)
@@ -222,6 +230,8 @@ gimp_plug_in_class_init (GimpPlugInClass *klass)
object_class->set_property = gimp_plug_in_set_property;
object_class->get_property = gimp_plug_in_get_property;
+ klass->set_i18n = gimp_plug_in_real_set_i18n;
+
/**
* GimpPlugIn:read-channel:
*
@@ -373,45 +383,21 @@ gimp_plug_in_get_property (GObject *object,
}
}
-
-/* public functions */
-
-/**
- * gimp_plug_in_set_translation_domain:
- * @plug_in: A #GimpPlugIn.
- * @domain_name: The name of the textdomain (must be unique).
- * @domain_path: (nullable): A file pointing to the compiled message catalog
- * (may be %NULL).
- *
- * Sets a textdomain for localisation for the @plug_in.
- *
- * This function adds a textdomain to the list of domains Gimp
- * searches for strings when translating its menu entries. There is no
- * need to call this function for plug-ins that have their strings
- * included in the 'gimp-std-plugins' domain as that is used by
- * default. If the compiled message catalog is not in the standard
- * location, you may specify an absolute path to another
- * location.
- *
- * This function can only be called in the
- * [vfunc@PlugIn.query_procedures] function of a plug-in.
- *
- * Since: 3.0
- **/
-void
-gimp_plug_in_set_translation_domain (GimpPlugIn *plug_in,
- const gchar *domain_name,
- GFile *domain_path)
+static gboolean
+gimp_plug_in_real_set_i18n (GimpPlugIn *plug_in,
+ const gchar *procedure_name,
+ gchar **gettext_domain,
+ gchar **catalog_dir)
{
- g_return_if_fail (GIMP_IS_PLUG_IN (plug_in));
- g_return_if_fail (domain_name != NULL);
- g_return_if_fail (domain_path == NULL || G_IS_FILE (domain_path));
+ /* Default to enabling localization by gettext. It will have the good
+ * side-effect of warning plug-in developers of the existence of the
+ * ability through stderr if the catalog directory is missing.
+ */
+ return TRUE;
+}
- g_free (plug_in->priv->translation_domain_name);
- plug_in->priv->translation_domain_name = g_strdup (domain_name);
- g_set_object (&plug_in->priv->translation_domain_path, domain_path);
-}
+/* public functions */
/**
* gimp_plug_in_set_help_domain:
@@ -890,13 +876,133 @@ _gimp_plug_in_read_expect_msg (GimpPlugIn *plug_in,
}
}
+gboolean
+_gimp_plug_in_set_i18n (GimpPlugIn *plug_in,
+ const gchar *procedure_name,
+ gchar **gettext_domain,
+ gchar **catalog_dir)
+{
+ gboolean use_gettext;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN (plug_in), FALSE);
+ g_return_val_if_fail (gettext_domain && *gettext_domain == NULL, FALSE);
+ g_return_val_if_fail (catalog_dir && *catalog_dir == NULL, FALSE);
+
+ if (! plug_in->priv->translation_domain_path ||
+ ! plug_in->priv->translation_domain_name)
+ gimp_plug_in_init_i18n (plug_in);
+
+ if (! GIMP_PLUG_IN_GET_CLASS (plug_in)->set_i18n)
+ {
+ use_gettext = FALSE;
+ }
+ else
+ {
+ use_gettext = GIMP_PLUG_IN_GET_CLASS (plug_in)->set_i18n (plug_in,
+ procedure_name,
+ gettext_domain,
+ catalog_dir);
+ if (use_gettext)
+ {
+ if (! (*gettext_domain))
+ *gettext_domain = g_strdup (plug_in->priv->translation_domain_name);
+
+ if (*catalog_dir)
+ {
+ if (g_path_is_absolute (*catalog_dir))
+ {
+ g_printerr ("[%s] The catalog directory set by set_i18n() is not relative: %s\n",
+ procedure_name, *catalog_dir);
+ g_printerr ("[%s] Localization disabled\n", procedure_name);
+
+ use_gettext = FALSE;
+ }
+ else
+ {
+ gchar *rootdir = g_path_get_dirname (gimp_get_progname ());
+ GFile *root_file = g_file_new_for_path (rootdir);
+ GFile *catalog_file;
+ GFile *parent;
+
+ catalog_file = g_file_resolve_relative_path (root_file, *catalog_dir);
+
+ /* Verify that the catalog is a subdir of the plug-in folder.
+ * We do not want to allow plug-ins to look outside their own
+ * realm.
+ */
+ parent = g_file_dup (catalog_file);
+ do
+ {
+ if (g_file_equal (parent, root_file))
+ break;
+ g_clear_object (&parent);
+ }
+ while ((parent = g_file_get_parent (parent)));
+
+ if (parent == NULL)
+ {
+ g_printerr ("[%s] The catalog directory set by set_i18n() is not a subdirectory: %s\n",
+ procedure_name, *catalog_dir);
+ g_printerr ("[%s] Localization disabled\n", procedure_name);
+
+ use_gettext = FALSE;
+ }
+
+ g_free (rootdir);
+ g_object_unref (root_file);
+ g_clear_object (&parent);
+ g_object_unref (catalog_file);
+ }
+ }
+ else
+ {
+ *catalog_dir = g_file_get_path (plug_in->priv->translation_domain_path);
+ }
+ }
+ }
+
+ if (use_gettext && ! g_file_test (*catalog_dir, G_FILE_TEST_IS_DIR))
+ {
+ g_printerr ("[%s] The catalog directory does not exist: %s\n",
+ procedure_name, *catalog_dir);
+ g_printerr ("[%s] Override method set_i18n() for the plug-in to customize or disable localization.\n",
+ procedure_name);
+ g_printerr ("[%s] Localization disabled\n", procedure_name);
+
+ use_gettext = FALSE;
+ }
+
+ if (! use_gettext)
+ {
+ g_clear_pointer (gettext_domain, g_free);
+ g_clear_pointer (catalog_dir, g_free);
+ }
+
+ return use_gettext;
+}
+
GimpProcedure *
_gimp_plug_in_create_procedure (GimpPlugIn *plug_in,
const gchar *procedure_name)
{
+ gchar *gettext_domain = NULL;
+ gchar *catalog_dir = NULL;
+
g_return_val_if_fail (GIMP_IS_PLUG_IN (plug_in), NULL);
g_return_val_if_fail (gimp_is_canonical_identifier (procedure_name), NULL);
+ if (_gimp_plug_in_set_i18n (plug_in, procedure_name, &gettext_domain, &catalog_dir))
+ {
+ bindtextdomain (gettext_domain, catalog_dir);
+#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+ bind_textdomain_codeset (gettext_domain, "UTF-8");
+#endif
+ textdomain (gettext_domain);
+
+ g_free (gettext_domain);
+ g_free (catalog_dir);
+ }
+
if (GIMP_PLUG_IN_GET_CLASS (plug_in)->create_procedure)
return GIMP_PLUG_IN_GET_CLASS (plug_in)->create_procedure (plug_in,
procedure_name);
@@ -932,12 +1038,6 @@ gimp_plug_in_register (GimpPlugIn *plug_in,
g_list_free_full (procedures, g_free);
- if (plug_in->priv->translation_domain_name)
- {
- _gimp_plug_in_domain_register (plug_in->priv->translation_domain_name,
- plug_in->priv->translation_domain_path);
- }
-
if (plug_in->priv->help_domain_name)
{
_gimp_plug_in_help_register (plug_in->priv->help_domain_name,
@@ -1216,7 +1316,22 @@ gimp_plug_in_proc_run_internal (GimpPlugIn *plug_in,
GPProcReturn *proc_return)
{
GimpValueArray *arguments;
- GimpValueArray *return_values = NULL;
+ GimpValueArray *return_values = NULL;
+ gchar *gettext_domain = NULL;
+ gchar *catalog_dir = NULL;
+
+ if (_gimp_plug_in_set_i18n (plug_in, gimp_procedure_get_name (procedure),
+ &gettext_domain, &catalog_dir))
+ {
+ bindtextdomain (gettext_domain, catalog_dir);
+#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+ bind_textdomain_codeset (gettext_domain, "UTF-8");
+#endif
+ textdomain (gettext_domain);
+
+ g_free (gettext_domain);
+ g_free (catalog_dir);
+ }
gimp_plug_in_push_procedure (plug_in, procedure);
@@ -1493,3 +1608,27 @@ gimp_plug_in_destroy_proxies (GHashTable *hash_table,
}
}
}
+
+static void
+gimp_plug_in_init_i18n (GimpPlugIn *plug_in)
+{
+ gchar *rootdir = g_path_get_dirname (gimp_get_progname ());
+ GFile *root_file = g_file_new_for_path (rootdir);
+ GFile *catalog_file = NULL;
+
+ g_return_if_fail (GIMP_IS_PLUG_IN (plug_in));
+
+ /* Default domain name is the program directory name. */
+ g_free (plug_in->priv->translation_domain_name);
+ plug_in->priv->translation_domain_name = g_path_get_basename (rootdir);
+
+ /* Default catalog path is the locale/ directory under the root
+ * directory.
+ */
+ catalog_file = g_file_resolve_relative_path (root_file, "locale");
+ g_set_object (&plug_in->priv->translation_domain_path, catalog_file);
+
+ g_free (rootdir);
+ g_object_unref (root_file);
+ g_object_unref (catalog_file);
+}
diff --git a/libgimp/gimpplugin.h b/libgimp/gimpplugin.h
index f8161b8b3d..a966e3e94d 100644
--- a/libgimp/gimpplugin.h
+++ b/libgimp/gimpplugin.h
@@ -127,6 +127,57 @@ struct _GimpPlugInClass
*/
void (* quit) (GimpPlugIn *plug_in);
+ /**
+ * GimpPlugInClass::set_i18n:
+ * @plug_in: a #GimpPlugIn.
+ * @procedure_name: procedure name.
+ * @gettext_domain: (out) (nullable): Gettext domain. If %NULL, it
+ * defaults to the plug-in name as determined by the
+ * directory the binary is called from.
+ * @catalog_dir: (out) (nullable): relative path to a subdirectory
+ * of the plug-in folder containing the compiled
+ * Gettext message catalogs. If %NULL, it defaults to
+ * "locale/".
+ *
+ * This method can be overridden by all plug-ins to customize
+ * internationalization of the plug-in.
+ *
+ * This method will be called before initializing, querying or running
+ * @procedure_name (respectively with [vfunc@PlugIn.init_procedures],
+ * [vfunc@PlugIn.query_procedures] or with the `run()` function set in
+ * `gimp_image_procedure_new()`).
+ *
+ * By default, GIMP plug-ins look up gettext compiled message catalogs
+ * in the subdirectory `locale/` under the plug-in folder (same folder
+ * as `gimp_get_progname()`) with a text domain equal to the plug-in
+ * name (regardless @procedure_name). It is unneeded to override this
+ * method if you follow this localization scheme.
+ *
+ * If you wish to disable localization or localize with another system,
+ * simply set the method to %NULL, or possibly implement this method
+ * to do something useful for your usage while returning %FALSE.
+ *
+ * If you wish to tweak the @gettext_domain or the @localedir, return
+ * %TRUE and allocate appropriate @gettext_domain and/or @localedir
+ * (these use the default if set %NULL).
+ *
+ * Note that @localedir must be a relative path, subdirectory of the
+ * directory of `gimp_get_progname()`.
+ *
+ * When localizing your plug-in this way, GIMP also binds
+ * @gettext_domain to the UTF-8 encoding.
+ *
+ * Returns: %TRUE if this plug-in will use Gettext localization. You
+ * may return %FALSE if you wish to disable localization or
+ * set it up differently.
+ *
+ * Since: 3.0
+ */
+ gboolean (* set_i18n) (GimpPlugIn *plug_in,
+ const gchar *procedure_name,
+ gchar **gettext_domain,
+ gchar **catalog_dir);
+
/* Padding for future expansion */
/*< private >*/
void (* _gimp_reserved1) (void);
@@ -144,9 +195,6 @@ GQuark gimp_plug_in_error_quark (void);
GType gimp_plug_in_get_type (void) G_GNUC_CONST;
-void gimp_plug_in_set_translation_domain (GimpPlugIn *plug_in,
- const gchar *domain_name,
- GFile *domain_path);
void gimp_plug_in_set_help_domain (GimpPlugIn *plug_in,
const gchar *domain_name,
GFile *domain_uri);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]