Skip to content

Instantly share code, notes, and snippets.

@timwhitlock
Last active October 17, 2023 17:28
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save timwhitlock/a079bbf4f87eb4c44db87d270c7119c2 to your computer and use it in GitHub Desktop.
Save timwhitlock/a079bbf4f87eb4c44db87d270c7119c2 to your computer and use it in GitHub Desktop.
Standalone version of Loco_hooks_LoadHelper
<?php
/*
Plugin Name: Loco Standalone Loader
Description: Mimics Loco_hooks_LoadHelper without dependency on the main plugin
Author: Tim Whitlock
Version: 1.0
*/
new LocoStandaloneLoadHelper;
/**
* Standalone version of Loco_hooks_LoadHelper in main plugin.
* Differences:
* - Manually hooked in constructor
* - Does not call `loco_constant`
* - Self-destructs if main plugin enabled
*/
class LocoStandaloneLoadHelper {
/**
* Singleton
* @var LocoStandaloneLoadHelper
*/
private static $hooked;
/**
* @var array [ $subdir, $domain, $locale ]
*/
private $context;
/**
* @var array
*/
private $lock = array();
/**
* @internal
*/
public function __construct(){
add_filter( 'theme_locale', array($this,'filter_theme_locale'), 10, 2 );
add_filter( 'plugin_locale', array($this,'filter_plugin_locale'), 10, 2 );
add_action( 'load_textdomain', array($this,'on_load_textdomain'), 10, 2 );
add_action( 'unload_textdomain', array($this,'on_unload_textdomain'), 10, 1 );
// maintain singleton
if( self::$hooked ){
self::$hooked->destroy();
}
self::$hooked = $this;
}
/**
* @internal
*/
private function destroy(){
remove_filter( 'theme_locale', array($this,'filter_theme_locale'), 10 );
remove_filter( 'plugin_locale', array($this,'filter_plugin_locale'), 10 );
remove_action( 'load_textdomain', array($this,'on_load_textdomain'), 10 );
remove_action( 'unload_textdomain', array($this,'on_unload_textdomain'), 10 );
if( self::$hooked === $this ){
self::$hooked = null;
}
}
/**
* Abandon this plugin if main Loco Translate plugin becomes enabled
* @return bool whether still required
*/
private function required(){
if( $hasLoco = class_exists('Loco_hooks_LoadHelper') ){
$this->destroy();
}
return ! $hasLoco;
}
/**
* `theme_locale` filter callback.
* Signals the beginning of a "load_theme_textdomain" process
*/
public function filter_theme_locale( $locale, $domain = '' ){
if( $this->required() ){
$this->context = array( 'themes', $domain, $locale );
unset( $this->lock[$domain] );
}
return $locale;
}
/**
* `plugin_locale` filter callback.
* Signals the beginning of a "load_plugin_textdomain" process
*/
public function filter_plugin_locale( $locale, $domain = '' ){
if( $this->required() ){
$this->context = array( 'plugins', $domain, $locale );
unset( $this->lock[$domain] );
}
return $locale;
}
/**
* `unload_textdomain` action callback.
* Lets us release lock so that custom file may be loaded again (hopefully for another locale)
*/
public function on_unload_textdomain( $domain ){
if( $this->required() ){
unset( $this->lock[$domain] );
}
}
/**
* `load_textdomain` action callback.
* Lets us load our custom translations before WordPress loads what it was going to anyway.
* We're deliberately not stopping WordPress loading $mopath, if it exists it will be merged on top of our custom strings.
* @return void
*/
public function on_load_textdomain( $domain, $mopath ){
if( ! $this->required() ){
return;
}
$key = '';
// domains may be split into multiple files
$name = pathinfo( $mopath, PATHINFO_FILENAME );
if( $lpos = strrpos( $name, '-') ){
$slug = substr( $name, 0, $lpos );
if( $slug !== $domain ){
$key = $slug;
}
}
// avoid recursion when we've already handled this domain/slug
if( isset($this->lock[$domain][$key]) ){
return;
}
// language roots
$wp_lang_dir = trailingslashit( WP_LANG_DIR );
$lc_lang_dir = defined('LOCO_LANG_DIR') ? trailingslashit(LOCO_LANG_DIR) : $wp_lang_dir.'loco/';
// if context is set, then a theme or plugin initialized the loading process properly
if( is_array($this->context) ){
list( $subdir, $_domain, $locale ) = $this->context;
$this->context = null;
// It shouldn't be possible to catch a different domain after setting context, but we'd better bail just in case
if( $_domain !== $domain ){
return;
}
$mopath = $lc_lang_dir.$subdir.'/'.$domain.'-'.$locale.'.mo';
}
// else load_textdomain must have been called directly to bypass locale filters
else {
$snip = strlen($wp_lang_dir);
// direct file loads must be under WP_LANG_DIR if we are to map them
if( substr( dirname($mopath).'/', 0, $snip ) === $wp_lang_dir ){
$mopath = substr_replace( $mopath, $lc_lang_dir, 0, $snip );
}
// else no way to map files from WP_LANG_DIR to LOCO_LANG_DIR
else {
return;
}
}
// Load our custom translations avoiding recursion back into this hook
$this->lock[$domain][$key] = true;
load_textdomain( $domain, $mopath );
}
}
@timwhitlock
Copy link
Author

This can be dropped into wp-content/mu-plugins so it will run before any other dynamically enabled plugins.

It has two main purposes.

  1. If you want to disable Loco Translate, but want to keep the custom loading functionality
  2. If you are suffering from the early loading problem, described here.

@danielvillacism
Copy link

Hi, does it still work?? thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment