Download Log in

58. Translation / String Hooks

Developer Guide

Form Forge exposes a small set of hooks so a multilingual stack (Lang Forge first-party, anything compatible by contract) can localize labels, placeholders, options, and form-level text. The hooks are passive — without a listener the original strings are emitted unchanged, so sites without a translation plugin keep working exactly the way they did before.

Filter: formforge_translate_string

Fires for every translatable string Form Forge renders (labels, placeholders, descriptions, option labels, submit button, success message, schedule message). A translation plugin attaches to this filter and returns the translated string for the current language.

php
add_filter('formforge_translate_string', function ($string, $key, $field_id, $form_id) {
    // $string   - the source-language string (e.g. "Имя")
    // $key      - which slot the string fills:
    //               'label', 'placeholder', 'description',
    //               'option_label:<value>',
    //               'submit_text', 'success_message', 'schedule_message'
    // $field_id - the field's `id` from wp_formforge_forms.fields. Empty
    //             string for form-level keys.
    // $form_id  - integer form id.
    if ($key === 'label' && $field_id === 'name') {
        // Look up your translation here.
        return my_plugin_translate($string, "form-{$form_id}-name");
    }
    return $string;
}, 10, 4);

Returning the original $string is the correct fallback when no translation is available — the renderer escapes the result, so the user never sees a missing-translation marker.

Action: formforge/strings_registered

Fires after a form is created or updated, with every translatable string in a single call. Listeners use this to register strings in their translation tables in batches.

php
add_action('formforge/strings_registered', function ($form_id, $strings) {
    // $strings is an array of:
    //   ['key' => '<key>', 'field_id' => '<field_id>', 'value' => '<source string>']
    foreach ($strings as $entry) {
        my_plugin_register_string($form_id, $entry['key'], $entry['field_id'], $entry['value']);
    }
}, 10, 2);

Re-fires on every save, so renamed labels and added options end up in your translation table without an explicit re-import. Listeners should treat registration as idempotent (INSERT IGNORE / upsert by (form_id, key, field_id)).

Action: formforge/strings_unregistered

Fires when a form is deleted, before the row is removed from wp_formforge_forms. Use this to clean up translations attached to the form.

php
add_action('formforge/strings_unregistered', function ($form_id) {
    my_plugin_delete_form_strings($form_id);
}, 10, 1);

Filter: formforge_current_language

Returns the visitor’s current language at form-render time. The result is emitted into the rendered form as a hidden formforge_lang input so the AJAX submit handler can resolve translations correctly (admin-ajax requests don’t carry the URL-prefix or ?lang= signal a multilingual plugin would normally use to detect the visitor’s language).

php
add_filter('formforge_current_language', function ($default, $form_id) {
    return my_plugin_get_current_language() ?: $default;
}, 10, 2);

Return '' (the default) to skip the hidden field entirely.

Filter: formforge_translate_lang_override

Fires inside formforge_translate_string listeners (when used together) with the visitor’s language as resolved from the submit payload. Translation plugins use this to override the runtime “current language” lookup during AJAX submit handling so success messages come back in the visitor’s language, not the site default.

php
add_filter('formforge_translate_lang_override', function ($lang, $form_id) {
    if (!empty($_POST['formforge_lang'])) {
        return sanitize_text_field(wp_unslash($_POST['formforge_lang']));
    }
    return $lang;
}, 10, 2);

Per-form string domain

The Lang Forge listener uses formforge: as the per-form string_domain in wp_lf_strings. This keeps every form’s strings grouped together and makes per-form cleanup a single DELETE WHERE string_domain = ? call. If you are writing a non-LangForge listener, follow the same convention so admin tools that aggregate across translation backends can match strings to forms reliably.

Stable string names

To keep translations stable across re-saves, Form Forge does not include the source-language text in its generated string names. The names are:

  • Field-level: field__ — e.g. field_name_label, field_email_placeholder, field_category_option_label:business
  • Form-level: — e.g. submit_text, success_message, schedule_message

Renaming a field’s id will reset its translation key (intentional, because the identity of the field changed). Renaming a label’s text without changing the id keeps the existing translation row in place, so the translator’s previous work is preserved (you may want to mark it for review — that’s a translation-plugin concern, not a Form Forge one).

Forge AI Assistant Online

Hi! I'm the Form Forge AI assistant. Ask me anything about the plugin — setup, features, troubleshooting, or development.

Just now
Powered by Forge AI · Browse docs