Internationalizing GIMP Scheme Plugins
Internationalizing makes a plugin’s GUI appear in the native language of the user.
Internationalizing is also known as localizing, or i18n. Internally GIMP uses the i18n system called “gettext.”
Internationalization process
/plugins.The process is:
- Mark user-facing GUI strings in the source like: _“Foo”
 - Call e.g. (script-fu-register-i18n “plug-in-my” “Standard”) at the bottom of your script.
 - Coordinate with translators to create one or more translation data files (.po “portable”)
 - Build and install translation data (.mo “machine”) in a “locale” subdirectory of your plugin’s main directory.
 
More details and examples follow.
    The Marker _
      
      
    
Use this in the Scheme source of a plugin to mark user-facing GUI strings that should be internationalized, i.e. translated into native language. Typically, you mark these strings:
- The label of the menu item of the plugin
 - Labels on widgets in a dialog for the plugin
 - Error and information messages to the user
 
The _ marker character must precede a double quote character. Example, to mark a pop-up menu widget:
SF-OPTION     _"Orientation"    '(_"Horizontal" _"Vertical")On such example, this means both the label of the pop-up widget, and the strings for the choices, should be translated.
    script-fu-register-i18n
      
      
    
This function is needed only for internationalization.
(script-fu-register-i18n "PDB name"
                        "domainName" | "Standard" | "None"
                        ["relative path"]) => voidWhen used, it must appear at the outermost lexical level of a script. That is, in the global environment, AFTER a call to script-fu-register- that defines the PDB procedure named in the first argument.
The function takes two or three arguments and returns nothing:
- The first argument is of type string and is the name of a PDB procedure;
 - The second argument is of type string and is either a domain name in the i18n system (the name of the translation file) OR “Standard” (file name where the PDB procedure is defined) OR, rarely, “None”;
 - The third (optional) argument is of type string and is a relative path to a “catalog” directory. The path must be below the parent directory of the file where the PDB procedure is defined. Anyway, you should rarely need it. When the third argument is not provided, the i18n system uses as “catalog” a standard directory named “locale”.
 
The Role of Translators
This briefly explains how translators work together with script authors.
You mark strings in the source.
A translator is usually fluent in English and one target language. You may need many translators for many target languages.
How translation files are created can differ between scripts. A person in the role of translator can use software tools to scan source scripts to produce .po files. It is also possible that the script author provides updated .po files or template .pot files for translation.
The .po files are text files. A .po file essentially has many pairs of English phrases with their translations. The “p” means “portable.” The .po files are usually kept as “generated” source in the source code repository.
Then the translator uses software tools to edit .po files, changing the translated second half of translation string pairs. (The second half was initially untranslated.)
When you change user-facing strings in the source code, translators can update the .po files, without starting the whole process over again.
At build time, another tool compiles the .po files into binary .mo files. The “m” means machine. The .mo files are more compact than the .mo files, but are not human readable. The .mo files are installed with a plugin.
Translation data files
Example for Use Case: Single Plugin
At the bottom of your script:
(script-fu-register-i18n "plugin-in-my" "Standard")where “plug-in-my” is the name of your plugin’s PDB procedure and run function (in Scheme.)
Install .mo files alongside your script, in a “locale” subdirectory.
.../plug-ins/my/my.scm
               /locale/es/LC_MESSAGES/my.mo
                      /de/LC_MESSAGES/my.moTo do that install, use this meson code in the meson.build file inside the source directory holding your translation data (es.po and de.po files, and the LINGUAS file):
i18n.gettext(
  "my",
  preset: 'glib',
  install_dir:
    get_option('prefix') / get_option('libdir') /
      'plug-ins' / 'my' / 'locale')The “install_dir” argument builds a path to the plugin’s installed directory, starting with the platform’s path to GIMP’s installed “plug-ins” directory. (GIMP’s meson.build files have that path in the “gimpplugindir” variable.)
Example for Use Case: Suite of Plugins
In this use case, a suite of plugins shares .po/.mo files. You do this when the suite of plugins use a same set of phrases, to simplify the work of translators.
Suppose you name the suite of plugins “mySuite”. The file named “mySuite.scm” defines two PDB procedures in the suite.
At the bottom of mySuite.scm, declare intent to translate:
(script-fu-register-i18n "plugin-in-my1" "mySuite")
(script-fu-register-i18n "plugin-in-my2" "mySuite")where “plug-in-my1” and “plug-in-my2” are the names of the suite’s PDB procedures and run functions (in Scheme.)
Install .mo files alongside your script, in a “locale” subdirectory.
.../plug-ins/mySuite/mySuite.scm
                    /locale/es/LC_MESSAGES/mySuite.mo
                           /de/LC_MESSAGES/mySuite.moTo do that install, use this meson code in the meson.build file inside the source directory:
i18n.gettext(
  "mySuite",
  preset: 'glib',
  install_dir:
    get_option('prefix') / get_option('libdir') /
      'plug-ins' / 'mySuite' / 'locale')The “install_dir” argument builds a path to the plugin suite’s installed directory, starting with the platform’s path to GIMP’s installed “plug-ins” directory. (In the meson build system, this construct is platform independent.)
In this use case, your source directory is like:
mySuite/ mySuite.scm   (defines two PDB procedures)
       / meson.build
       / es.po         (from translators)
       / de.po
       / LINGUAS       (a text file that lists "es" and "de")Example for Use Case: Suite of Plugins with Separate Plugin Files
This use case expands on the previous use case.
In this use case, there is a suite of PDB procedures, but one is defined in a separate source file. The file named “mySuite.scm” defines two PDB procedures in the suite. The file named “mySuitMore.scm” defines another PDB procedure in the suite. Again, all the PDB procedures use same phrases.
At the bottom of mySuiteMore.scm, declare intent to translate:
(script-fu-register-i18n "plugin-in-my3" "mySuiteMore")Note that In this use case, the domain name is different, so the translation data will be duplicated and installed to two places. (It may seem wasteful to duplicate a file, but for now, there is no other way to do this.)
The installed files:
.../plug-ins/mySuite/mySuite.scm
                    /locale/es/LC_MESSAGES/mySuite.mo
                           /de/LC_MESSAGES/mySuite.mo
.../plug-ins/mySuiteMore/mySuiteMore.scm
                        /locale/es/LC_MESSAGES/mySuiteMore.mo
                               /de/LC_MESSAGES/mySuiteMore.moTo do that install, you must duplicate the meson code in the meson.build file where the translation data is (alongside mySuite.scm):
i18n.gettext(
  "mySuite",            # first domain name
  preset: 'glib',
  install_dir:
    get_option('prefix') / get_option('libdir') /
      'plug-ins' / 'mySuite' / 'locale')
i18n.gettext(
  "mySuiteMore",       # second domain name
  preset: 'glib',
  install_dir:         # second, duplicate location
    get_option('prefix') / get_option('libdir') /
      'plug-ins' / 'mySuiteMore' / 'locale')In this use case, your source directory could be like:
mySuite/ mySuite.scm     (defines two PDB procedures)
       / mySuiteMore.scm (defines another PDB procedure)
       / meson.build
       / es.po           (from translators)
       / de.po
       / LINGUAS         (a text file listing "es" and "de")Example for Use Case: Plugins not Internationalized
When you don’t want to internationalize your plugin, just forego the instructions for the other use cases.
You don’t need to call “script-fu-register-i18n”. You can call it to document that your plugin is not internationalized:
(script-fu-register-i18n "plugin-in-my" "None")Your plugin will only appear in the language of the user-facing strings of the source code (which need not be English.)
Internationalizing of GIMP own Plugins in /scripts
As said, it is not possible to localize third-party old-style scripts.
That is because the extension-script-fu plugin has its own i18n declaration that is used for all served scripts. So served scripts all use the domain gimp30-script-fu and the only one corresponding translation data installed with GIMP, which is /usr/local/share/locale/es/LC_MESSAGES/gimp30-script-fu.mo.
See the use of the macro DEFINE_STD_SET_I18N in the source files script-fu.c and script-fu-intl.h if you are curious.