WP Residence Help WP Residence Help

  • WpEstate
  • WPRESIDENCE
  • Video Tutorials
  • Client Support
  • API
Home / 30. WPResidence Translate Plugin / Gettext Pipeline & MO Files — Developer Guide

Gettext Pipeline & MO Files — Developer Guide

11 views 0

This article traces the full path from a stored translation row to a translated label rendered on the page: how MO files are compiled, where they land on disk, how the plugin injects them into WordPress’s textdomain loader, and how runtime gettext filters behave. It is the developer companion to the user-facing article. For product positioning see multi-language real estate website.

Relevant Files

File Role
includes/translation-directories.php Candidate list + writable-directory selection + caching.
includes/translation-files.php Filename builder, locale normaliser, loaded-file registry.
includes/translation-runtime.php Active-locale resolver, DB lookup cache, admin-only runtime provider.
includes/translation-hooks.php Filter callbacks attached to load_textdomain_mofile, override_load_textdomain, gettext, gettext_with_context.
includes/admin/string-export.php Compiles DB rows into MO files via the WordPress POMO classes.
includes/plugin-bootstrap.php Registers the four gettext-related filters.

Filter Registration

Inside wpr_translate_bootstrap_plugin():

add_filter( 'load_textdomain_mofile',  'wpr_translate_override_mofile', 10, 2 );
add_filter( 'override_load_textdomain', 'wpr_translate_override_load_textdomain', 10, 3 );
add_filter( 'gettext',                 'wpr_translate_filter_gettext', 10, 3 );
add_filter( 'gettext_with_context',    'wpr_translate_filter_gettext_with_context', 10, 4 );
add_filter( 'locale',                  'wpr_translate_filter_frontend_locale', 5 );

Compilation: DB → MO

wpr_translate_admin_generate_translation_files() (in string-export.php) is the entry point. It:

  1. Loads wpr_translate_languages and resolves the default language code.
  2. Verifies the strings table exists and the processed column is present.
  3. Selects every row from {prefix}wpestate_translation_strings (non-empty translation only).
  4. Groups results by context then language_code.
  5. Maps each context to a text domain by re-running wpr_translate_admin_get_scan_targets() and reading $target[‘domain’]. If that lookup fails, the context slug is used as fallback.
  6. Calls wpr_translate_prepare_custom_translations_directory() to ensure the output directory exists and is writable.
  7. Uses WordPress’s MO and Translation_Entry classes (wp-includes/pomo/mo.php, entry.php) to build each MO file in memory:
    • Imports an existing compiled MO from wpr_translate_locate_custom_mofile() so previous translations are retained.
    • Sets headers: Project-Id-Version, POT-Creation-Date, PO-Revision-Date, Language, Content-Type: text/plain; charset=UTF-8, Plural-Forms: nplurals=2; plural=(n != 1);
    • Adds entries with singular/translations.
  8. Writes via $mo->export_to_file( $file_path ) using wpr_translate_build_custom_mofile_path().
  9. Updates processed = 1 on exported rows in a single prepared UPDATE with IN() placeholders.

The return value is array( 'files' => int, 'strings' => int, 'errors' => string[] ).

Output File Naming

Built by wpr_translate_build_custom_mofile_path( $domain, $locale ):

  • Domain passes through wpr_translate_normalize_translation_domain() (lowercased, non-[a-z0-9_\-.] replaced with -).
  • Locale passes through wpr_translate_normalize_translation_locale() (language part lowercased, region/script parts uppercased, dashes converted to underscores).
  • Final name: {directory}/{domain}-{locale}.mo (example: wpresidence-fr_FR.mo).

Output Directory Resolution

wpr_translate_get_custom_translations_directory_candidates() produces, in order:

  1. {stylesheet_directory}/languages/wpr — child theme first.
  2. {template_directory}/languages/wpr — parent theme next.
  3. {WPR_TRANSLATE_PLUGIN_ROOT}/languages/wpr — plugin fallback.

wpr_translate_select_preferred_translations_directory() prefers writable theme directories, then any other writable candidate. The chosen path is cached in $GLOBALS['wpr_translate_custom_directory'] and mirrored to the wpr_translate_custom_directory option. Two filters expose control:

  • wpr_translate_custom_translations_directory_candidates — modify the candidate list.
  • wpr_translate_custom_translations_directory — modify the final selection (receives $cached_directory, $candidates).

 

Load-Order Override

When WordPress asks for a textdomain’s MO file, load_textdomain_mofile is filtered first. wpr_translate_override_mofile( $mofile, $domain ) substitutes the plugin-generated MO file (via wpr_translate_locate_custom_mofile()) when one exists. wpr_translate_override_load_textdomain( $override, $domain, $mofile ) then records the load in the global registry ($GLOBALS[‘wpr_translate_loaded_mofiles’]) so runtime code can tell whether a custom MO is active for a (domain, locale) pair.

The registry entry shape:

array(
  'domain' => 'wpresidence',
  'locale' => 'fr_FR',
  'path'   => '/abs/path/to/wpresidence-fr_FR.mo',
  'source' => 'custom' | 'wordpress',
);

wpr_translate_has_custom_mofile_loaded( $domain, $locale ) returns true only when source === ‘custom’.

Runtime Gettext Filters

wpr_translate_filter_gettext() and wpr_translate_filter_gettext_with_context() (in translation-hooks.php) both delegate to wpr_translate_maybe_provide_translation( $translation, $text, $domain ) defined in translation-runtime.php.

That provider is intentionally restrictive:

  1. Skips when $translation !== $text (something else already translated).
  2. Calls wpr_translate_runtime_should_use_database_translations() which returns true only when the current request is the strings editor page ($_GET['page'] === 'wpr-translate-strings' or the same key in $_POST).
  3. Skips when a custom MO is already loaded for the domain/locale (wpr_translate_has_custom_mofile_loaded()).
  4. Skips when the domain has no DB rows for the active language (wpr_translate_domain_has_runtime_translations(), cached per request).
  5. Looks up the row via wpr_translate_lookup_runtime_translation() — indexed by context IN (candidate contexts) AND name = 'str_' . md5($original) AND language_code = $code, with a LIKE fallback for legacy context values.

Net effect: front-end requests always go through normal gettext + compiled MO files. Database-backed translations are a live-preview layer that only kicks in while the translator is editing.

Active Locale Resolution

wpr_translate_get_active_locale() (recursion-safe via a static flag) follows this order:

  1. On admin non-AJAX requests → determine_locale() / get_locale().
  2. wpr_translate_get_current_language() (router-resolved language).
  3. wpr_translate_get_context_language() (admin-selected editing language).
  4. wpr_translate_get_default_language().
  5. WordPress fallback.

wpr_translate_get_active_language_code() mirrors the same chain for language codes.

Options Used

Option Purpose
wpr_translate_languages Languages registry — read during compile.
wpr_translate_custom_directory Cached output directory for generated MO files.
wpr_translate_scan_state Scanner mtime + language signatures (not read at compile time).

Hooks Summary

Hook Type Callback / Purpose
load_textdomain_mofile filter wpr_translate_override_mofile — substitute generated MO.
override_load_textdomain filter wpr_translate_override_load_textdomain — record load outcome.
gettext filter wpr_translate_filter_gettext → runtime DB lookup (editor only).
gettext_with_context filter wpr_translate_filter_gettext_with_context (same logic, with context).
locale filter wpr_translate_filter_frontend_locale — align frontend locale with current language.
wpr_translate_runtime_translation filter Applied to DB-resolved replacement before return.
wpr_translate_custom_translations_directory filter Final output directory for generated MO files.
wpr_translate_custom_translations_directory_candidates filter Candidate directory list.

Debug Hook

plugin-bootstrap.php also attaches wpr_debug_translations to gettext at priority 999 when debug logging is enabled. Use this when tracing why a string falls back to English.

 

Further Reading

  • String Scanner — how rows land in the strings table in the first place.
  • Translating Theme & Plugin Strings — the admin UI and bulk actions.
  • Automatic Translation — the provider-level API integration.

See also our multi-language real estate website guide.

30. WPResidence Translate Plugin

Related Articles

  • String Scanner — Developer Guide
  • The String Scanner
  • Gettext & MO Files — Making Translations Appear on the Front End
  • Automatic Translation — Developer Guide

WP Residence Documentation

  • 01. Getting Started
    • How to Get Support
    • Get your buyer license code.
    • Use SSL / https
    • Server / Theme Requirements
  • 02. Installation & Setup
  • 03. Installation FAQ
  • 06. Search & Filtering
    • Advanced Search Display Settings
    • Advanced Search Form
    • Geolocation Search for Half Map
    • Save Search Theme Options
    • Advanced Search Colors
  • 09. Agent, Agency & Developers
  • 08. Property Pages & Layouts
  • 07. Property Lists, Categories & Archive
  • 13. WPResidence Elementor Studio
  • 10. Blog Posts & Blog List
  • 11. Shortcodes
    • Contact Form
    • Featured Agency/Developer
    • Membership Packages
    • Testimonials
    • Google Map with Property Marker
    • Listings per Agent, Agency or Developer
    • Display Categories
    • Agent List
    • Recent Items Slider
    • Recent items
    • List Properties or Articles by ID
    • Featured Agent
    • Featured Article
    • Featured Property
    • Login & Register Form
    • Icon Content Box Shortcode
  • 12. Widgets
  • 04. Theme Options & Global Settings
    • General Settings
    • User Types Settings
    • Appearance
    • Logos & Favicon
    • Header
    • Footer Style and Colors
    • Price & Currency
    • Property Custom Fields
    • Features & Amenities
    • Listing Labels
    • Theme Slider
    • Permalinks
    • Splash Page
    • Social & Contact
    • Map Settings
    • Pin Management
    • How read from file works
    • General Design Settings
    • Custom Colors Settings
    • Header Design & Colors
    • Mobile Menu Colors
    • User Dashboard Colors
    • Print PDF Design
    • Property, Agent, Blog Lists Design Settings
    • Sidebar Widget Design
    • Font management
    • How to add custom CSS
    • Custom Property Card Unit – Beta version
    • Email Management
    • Import & Export theme options
    • reCaptcha settings
    • YELP API Integration
    • iHomefinder Optima Express IDX
    • MEMBERSHIP & PAYMENT Settings
    • Property Submission Page
    • PayPal Setup
    • Stripe Setup
    • Wire Transfer Payment Method
  • 20. Translations & Languages
  • 26. FAQ
  • 10. Pages
  • 11. Header
  • 12. Footer
  • 05. Maps & Location Settings
  • 18. Payments & Monetization
  • Plugins
    • 19. Included Plugins
    • 22. Third Party Plugins – IDX Compatibility
    • 21. Third-Party Plugins – Multi-Language
    • 23. Third party Plugins – Other
  • Technical
    • 24. Technical how to | Custom Code Required
    • 25. Technical: Child Theme

Join Us On

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