Lang Forge ships a built-in listener that picks up Form Forge’s translation hooks and exposes form labels / placeholders / options / submit text / success messages in the standard String Translation admin page. The integration class is LF_FormForge_Integration (includes/class-formforge-integration.php), loaded on plugins_loaded after the regular LF_String_Translation boot.
Hook contract
Form Forge emits three hooks; Lang Forge listens on all three.
| Hook | Direction | Used for |
|---|---|---|
do_action('formforge/strings_registered', $form_id, $strings) | FF → LF | Idempotent batch register on save |
do_action('formforge/strings_unregistered', $form_id) | FF → LF | Domain cleanup on delete |
apply_filters('formforge_translate_string', $string, $key, $field_id, $form_id) | FF → LF | Per-string lookup at render time |
apply_filters('formforge_current_language', $default, $form_id) | LF → FF | Render-time language hint for AJAX submit roundtrip |
apply_filters('formforge_translate_lang_override', $lang, $form_id) | LF → FF (via own hook) | AJAX-context override for success_message |
Per-form domain
Lang Forge uses formforge: as the string_domain in wp_lf_strings so:
- Each form’s strings are grouped together on the String Translation admin page
- A form delete is a single
DELETE WHERE string_domain = ?cleanup - Two forms can share the same source string with different translations (e.g. “Submit” can be translated differently per form)
Stable string names
The integration uses these names so renaming a label’s text without changing its field['id'] does not invalidate existing translations:
- Field-level:
field_—_ field_email_label,field_category_option_label:business - Form-level:
—submit_text,success_message,schedule_message
Example: writing your own listener (replacing or extending the built-in)
// Replace the bundled listener with a custom one.
remove_filter('formforge_translate_string', [LF_FormForge_Integration::instance(), 'translate'], 10);
add_filter('formforge_translate_string', function ($string, $key, $field_id, $form_id) {
// Custom resolution: route through your own translation memory, glossary, etc.
$current_lang = LF_Core::instance()->get_current_language();
$default_lang = LF_Core::instance()->get_default_language();
if ($current_lang === $default_lang) return $string;
$name = $field_id !== '' ? "field_{$field_id}_{$key}" : $key;
$domain = "formforge:{$form_id}";
return my_custom_lookup($string, $name, $domain, $current_lang) ?: $string;
}, 10, 4);General extensibility — registering form-style strings from other plugins
The same domain convention works for any plugin that wants to register and translate per-instance strings. Pick a unique domain prefix (mailpoet:42, learndash:97, etc.) and re-use the LF_String_Translation API directly:
// Register a string at instance creation time
LF_String_Translation::instance()->register_string(
'Welcome message text', // value
'instance_42_welcome', // name (stable)
'mycart:42', // domain (per-instance)
LF_Core::instance()->get_default_language()
);
// Translate at render time
$translated = LF_String_Translation::instance()->translate(
$original_string,
'instance_42_welcome',
'mycart:42'
);This pattern (the one Form Forge uses) keeps strings discoverable in the admin, scoped per instance, and cleanly removable on delete. It is the recommended way for any third-party plugin to integrate with Lang Forge for per-instance string translation.
—