WP Residence Help WP Residence Help

  • WPRESIDENCE
  • Video Tutorials
  • Client Support
  • API
Home / WPEstate Translate Plugin / Translating Theme & Plugin Strings — Developer Guide

Translating Theme & Plugin Strings — Developer Guide

196 views 0

This article documents how the string translation subsystem of the WPEstate Translate plugin (folder wpestate-translate) (text domain wpr-translate) is wired internally: the admin screen, the storage table, how rows land there, and how the admin runtime provides translations back to gettext when editing. Read the user-facing version for the day-to-day workflow and the broader multi-language real estate website guide for product context.

CONTENT

  • Files Involved
  • Storage Table
  • Admin Menu Page
  • Form Actions on the Page
  • AJAX Endpoints
  • Scan Pipeline
  • Generating MO Files
  • Theme Admin Strings (JSON-Declared)
  • Runtime Lookup
  • Extension Points
  • Deletion & Reset
  • UTF-8 Safety
  • Further Reading

Files Involved

File Responsibility
includes/admin/views/string-translation.php The admin grid view. Renders the filter bar, scan/generate buttons, and per-language textarea grid.
includes/admin/views/string-helpers.php View-side helpers (e.g. option-value lookup by dotted path for theme admin strings).
includes/admin/string-actions.php Handles the Delete selected translations bulk form submit.
includes/admin/string-database.php Table existence check and the lazy column upgraders for processed, msgctxt and plural.
includes/admin/string-query.php Request parsing (filters, pagination) and query assembly for the grid.
includes/admin/string-storage.php Locale/code maps, row merge/insert/update batching.
includes/admin/string-export.php MO file generation from stored rows.
includes/admin/string-targets.php Builds the list of scan targets (theme, parent theme, active plugins).
includes/admin/theme-strings.php Imports theme admin strings declared in wpr/theme_admin_strings.json.
includes/admin/theme-strings-helpers.php Flatten/expand wildcards for the JSON theme-admin string map.
includes/theme-widget-strings.php / includes/admin/theme-widget-strings.php Widget title and text collectors.
includes/admin/widget-instance-strings.php Widget-instance specific string emitters (sidebar label metadata).
includes/string-catalog.php, string-catalog-commands.php, string-catalog-export.php, string-catalog-filesystem.php, string-catalog-normalization.php String Catalog layer: scope discovery, refresh (scan), query, delete, export, file fingerprints and translation-file precedence. The admin handlers delegate to it.

Storage Table

All strings live in {$wpdb->prefix}wpestate_translation_strings. Relevant columns:

string_id       BIGINT primary key
context         e.g. "theme:wpresidence", "plugin:woocommerce"
name            str_<md5(key)> or a dotted theme-option key
msgctxt         gettext context ('' when the string has none)
value           the source (msgid) string
plural          msgid_plural, stored on the singular row (NULL otherwise)
language_code   target language code, one row per language per string
translation     translated string for this language_code
status          0 = untranslated, 1 = translated / default
processed       0 = needs export to MO, 1 = already exported
translator_id   BIGINT, nullable
updated_at      DATETIME
md5             md5 of the source value, used to detect rewording
UNIQUE KEY uniq_string (context, name, language_code)

A string therefore maps to N rows, one per configured language. The default language row always has status = 1 and stores the source value verbatim; it is never exported (see Generating MO Files). Strings are keyed by msgctxt plus msgid, the same key gettext uses, so the same text under two _x() contexts stays two strings. On installs created before 1.0.9 the processed, msgctxt and plural columns are added lazily by wpr_translate_admin_ensure_processed_column(), wpr_translate_admin_ensure_msgctxt_column() and wpr_translate_admin_ensure_plural_column() on the next scan or export. Contexted strings scanned before 1.0.9 need a rescan.

Admin Menu Page

Registered in includes/admin/menu.php at slug wpr-translate-strings with the render callback wpr_translate_admin_render_strings_page. The render callback delegates to includes/admin/views/string-translation.php with a $data payload: strings, languages, filters, domains, pagination, default_language_code, range_start, range_end, displayed.

Per-request data is built by wpr_translate_admin_get_strings_for_view( $filters, $pagination ) in string-query.php. Pagination defaults to 20 rows and can be filtered with wpr_translate_strings_per_page.

Form Actions on the Page

Three POST forms live inside string-translation.php. Each posts back to the same admin page with its own nonce:

Nonce field Action Handler
wpr_translate_scan_nonce Scan theme + plugins wpr_translate_admin_scan_strings()
wpr_translate_generate_nonce Build MO files wpr_translate_admin_generate_translation_files()
wpr_translate_reset_nonce Clear all stored strings wpr_translate_admin_reset_strings()
wpr_translate_delete_nonce Delete selected rows wpr_translate_admin_handle_delete_strings()

All handlers are wired through wpr_translate_admin_handle_strings_actions() which in turn is attached inside the admin bootstrap for the strings page.

AJAX Endpoints

  • wp_ajax_wpr_translate_save_translation → wpr_translate_admin_ajax_save_translation — inline save from textarea blur.
  • wp_ajax_wpr_translate_auto_translate_strings → wpr_translate_admin_ajax_auto_translate_strings — auto-translate button on the strings page.

Scan Pipeline

  1. wpr_translate_string_catalog_refresh() calls wpr_translate_admin_scan_strings(), which loads wpr_translate_languages and the scan targets.
  2. wpr_translate_admin_get_scan_targets() builds one target per source that has a readable languages/ directory: the active (child) theme, the parent theme, and every active plugin. Multisite merges active_sitewide_plugins. Single-file plugins are skipped.
  3. For each target the scanner fingerprints the eligible files in languages/ (wpr_translate_string_catalog_scope_fingerprint()) and skips the target via wpr_translate_scan_state when the fingerprint and the language signature (md5(wp_json_encode($languages))) are unchanged.
  4. wpr_translate_admin_collect_strings_from_path() locates .mo/.po files and extracts entries. It skips the folder where the plugin writes its own generated files. The key is msgctxt + “\4” + msgid (just the msgid when there is no context); entries are hashed by md5($context . ‘|’ . $key) and the name column is str_<md5($key)>. A msgid_plural is kept on the singular row.
  5. wpr_translate_admin_merge_detected_strings() folds overlapping results.
  6. wpr_translate_string_catalog_persist_scope() hands each scope to wpr_translate_admin_persist_detected_strings(), which looks up existing rows via a chunked SELECT IN() query, updates rows in place, and batches inserts 50 rows at a time inside a transaction.

Generating MO Files

wpr_translate_admin_generate_translation_files() (called through wpr_translate_string_catalog_export()) builds each .mo only from the stored rows, so a translation cleared in the grid leaves the file on the next Generate. Plural pairs are written as one plural entry; a pair whose plural form is untranslated is left out so the shipped catalog still answers _n(). Default-language rows are not exported, and a default-language .mo left by an older version is deleted on the next export (includes/admin/string-export.php). Files go to the first writable generated-files folder, checked in the child theme, then the parent theme, then the plugin.

Theme Admin Strings (JSON-Declared)

A theme can declare admin-option strings to be translated via wp-content/themes/<theme>/wpr/theme_admin_strings.json. wpr_translate_admin_import_theme_admin_strings() reads the file, flattens nested paths, expands wildcards against live option values, and stores the source domain in wpr_translate_theme_admin_strings_domain (with wpr_translate_theme_admin_strings_hash for change detection). The view then uses wpr_translate_admin_get_option_value_by_path() to resolve dotted names (e.g. wpestate_labels.search_button) against the current option value, so the grid always shows the live admin string.

Runtime Lookup

The generated MO file is an overlay. The override_load_textdomain callback loads it ahead of the theme or plugin catalog, and WordPress still loads the shipped file behind it, so any string the overlay does not contain falls through to the shipped (or Loco-edited) translation. The default language therefore always comes from the shipped .po/.mo files and the Theme Options values (see the Gettext Pipeline & MO Files article).

The gettext, gettext_with_context, ngettext and ngettext_with_context filters (registered in includes/plugin-bootstrap.php, callbacks in includes/translation-hooks.php) call wpr_translate_maybe_provide_translation(), which reads the strings table through wpr_translate_lookup_runtime_translation(). The gate wpr_translate_runtime_should_use_database_translations() returns true:

  • on the front end, when the current language is not the default language;
  • in wp-admin, only when $_GET[‘page’] or $_POST[‘page’] equals wpr-translate-strings, so edits preview without recompiling MO files.

The gettext context is passed to the lookup, so _x() and _nx() strings resolve against their msgctxt.

Extension Points

  • wpr_translate_strings_per_page — int filter controlling the grid page size (default 20).
  • wpr_translate_runtime_translation — filter applied after a runtime lookup; arguments are $replacement, $original, $domain, $language_code, $msgctxt.
  • wpr_translate_custom_translations_directory_candidates / wpr_translate_custom_translations_directory — control where generated MO files are written. The chosen path is cached in the wpr_translate_custom_directory option and reselected when it is no longer a candidate (for example after a theme switch).

Deletion & Reset

  • Selected rows — wpr_translate_admin_handle_delete_strings() decodes base64-JSON payloads submitted via strings[], validates nonce wpr_translate_delete_strings, and calls $wpdb->delete() per (context, name).
  • Clear all — wpr_translate_admin_reset_strings() calls wpr_translate_string_catalog_delete( array( ‘all’ => true ) ), which runs DELETE FROM {table}, wipes wpr_translate_scan_state so the next scan runs in full, and deletes the generated .mo files from the custom translations directory.

UTF-8 Safety

Language codes and locales are normalized (lowercased, dash-to-underscore) but names, values, and translations are stored verbatim. Do not pass the value or translation through sanitize_title() in extension code — non-Latin characters must be preserved.

Further Reading

  • String Scanner — target construction and scan-state cache internals.
  • Gettext Pipeline & MO Files — filter hooks and compiled-file layout.
  • Automatic Translation — provider wiring and OpenAI request details.

Product information for the plugin is available on the multi-language real estate website page.

WPEstate Translate Plugin

Related Articles

  • String Scanner — Developer Guide
  • The String Scanner
  • Gettext Pipeline & MO Files — Developer Guide
  • Gettext & MO Files — Making Translations Appear on the Front End
MLSImportWordPress IDX Plugin for MLS ListingsImport MLS listings into your WordPress website, customize your property pages, and keep your inventory updated.Start free 30-day trial →

Help Categories

  • 51 Getting Started
  • 72 Installation & Setup
  • 243 Installation FAQ
  • 18Agent, Agency & Developers
  • 5Blog Posts & Blog Lists
  • 39Elementor Shortcodes Built-In
  • 56FAQ
  • 15Footer
  • 37Header
  • 2IDX & MLSImport
  • 23Maps & Location Settings
  • 21Multi-Language - Third Party Plugins
  • 7Other Third party Plugins
  • 21Pages
  • 4Payments & Monetization
  • 20Property Lists, Categories & Archive
  • 37Property Pages & Layouts
  • 32Search & Filtering
  • 162Technical how to | Custom Code
  • 8Technical: Actions and filters
  • 7Technical: Child Theme
  • 86Theme Options & Global Settings
  • 6Translations & Languages
  • 16WPBakery Shortcodes
  • 50WPEstate Translate Plugin
  • 51WPResidence / WPEstate CRM
  • 50WPResidence 5.0 Documentation
  • 9WPResidence Elementor Studio

Join Us On

Powered by WP Estate - All Rights Reserved
  • WPRESIDENCE
  • Video Tutorials
  • Client Support
  • API