register_scripts(); $this->register_styles(); } /** * Register scripts * * @param array $scripts * * @return void */ public function register_scripts() { $scripts = $this->get_scripts(); foreach ( $scripts as $handle => $script ) { $deps = isset( $script['deps'] ) ? $script['deps'] : []; $in_footer = isset( $script['in_footer'] ) ? $script['in_footer'] : true; $version = isset( $script['version'] ) ? $script['version'] : $this->get_version( $script['src'] ); $deps = $this->get_dependencies( $script['src'], $deps ); wp_register_script( $handle, $script['src'], $deps, $version, $in_footer ); } } /** * Register styles * * @param array $styles * * @return void */ public function register_styles() { $styles = $this->get_styles(); foreach ( $styles as $handle => $style ) { $deps = isset( $style['deps'] ) ? $style['deps'] : false; $version = aisentic()->version; wp_register_style( $handle, $style['src'], $deps, $version ); } } /** * Get script and style file dependencies * * @param string $file_name * @param array $deps * * @return array */ private function get_dependencies( $file_name, $deps = [] ) { $assets = $this->get_file_assets( $file_name ); $assets_deps = ! empty( $assets['dependencies'] ) ? $assets['dependencies'] : []; $merged_deps = array_merge( $assets_deps, $deps ); return $merged_deps; } /** * Get file assets * * @param string $file_name * * @return array */ private function get_file_assets( $file_url ) { $file = $this->get_file_path( $file_url ); $assets = []; if ( file_exists( $file ) ) { $assets = include $file; } return $assets; } /** * Get script file version * * @param string $file_name * * @return string */ private function get_version( $file_name ) { $assets = $this->get_file_assets( $file_name ); $assets_vers = ! empty( $assets['version'] ) ? $assets['version'] : aisentic()->version; return $assets_vers; } /** * Get file path from url * * @param string $url * * @return string */ private function get_file_path( $url ) { // Check if the URL is valid if ( ! filter_var( $url, FILTER_VALIDATE_URL ) ) { return false; } // Parse the URL $url_parts = wp_parse_url( $url ); // Check if the URL has a path component if ( ! isset( $url_parts['path'] ) ) { return false; // URL does not contain a path } $clean_path = str_replace( '.js', '.asset.php', $url_parts['path'] ); // Get the file path from the URL path. if ( isset( $_SERVER['DOCUMENT_ROOT'] ) ) { $file_path = sanitize_text_field( wp_unslash( $_SERVER['DOCUMENT_ROOT'] ) ) . $clean_path; } else { $file_path = ''; // Or another appropriate fallback. } // Check if the file exists if ( ! file_exists( $file_path ) ) { return false; // File does not exist } return $file_path; } /** * Enqueue the i18n loader script and inject its state via wp_localize_script. * * Shared by the admin and frontend asset classes so both the dashboard and the * public storefront widget resolve translations from the same * `window.aisenticI18nLoader` global. The `aisentic-i18n-loader` handle must be * registered by the caller's get_scripts() and set as a dependency of the entry * bundle, or the bundle's i18n fetch fails with "I18n loader is not available". * * @return void */ protected function enqueue_i18n_loader(): void { $locale = get_locale(); $domain = 'aisentic'; $translation_map = $this->get_translation_map( $locale, $domain ); wp_enqueue_script( 'aisentic-i18n-loader' ); wp_localize_script( 'aisentic-i18n-loader', 'aisenticI18nLoader', [ 'state' => [ 'baseUrl' => content_url( 'languages/' ), 'locale' => $locale, 'domainMap' => (object) [], 'domainPaths' => (object) [], 'translationMap' => (object) $translation_map, ], ] ); } /** * Build a hash-to-url map for available translation JSON files. * * Scans standard WP languages directories and the plugin's own languages * folder for files matching {domain}-{locale}-{hash}.json, then returns * an associative array of hash => absolute URL so the JS loader can fetch * the correct file without constructing a guessed path. * * @param string $locale WP locale string (e.g. "fr_FR"). * @param string $domain Plugin text domain. * * @return array Map of hash => URL. */ protected function get_translation_map( string $locale, string $domain ): array { // Cache the hash->URL map so the per-request glob() scans below run only // once per plugin version + locale. The version is part of the key so a // new build (which changes the hashed filenames) misses the stale cache // automatically instead of serving filenames that 404. $cache_key = 'aisentic_i18n_map_' . md5( aisentic()->version . '|' . $domain . '|' . $locale ); $cached = get_transient( $cache_key ); if ( is_array( $cached ) ) { return $cached; } $map = []; $dirs = $this->get_translation_directories(); $prefix = $domain . '-' . $locale . '-'; foreach ( $dirs as $dir ) { if ( ! is_dir( $dir['path'] ) ) { continue; } $files = glob( $dir['path'] . $prefix . '*.json' ); if ( ! $files ) { continue; } foreach ( $files as $file ) { $filename = basename( $file, '.json' ); // Hash is everything after "{domain}-{locale}-". if ( strpos( $filename, $prefix ) === 0 ) { $hash = substr( $filename, strlen( $prefix ) ); if ( $hash ) { $map[ $hash ] = $dir['url'] . basename( $file ); } } } } // Do not pin an empty map for a full hour: a fresh install with no // translation files yet would otherwise hide translations installed // later the same hour (Loco / language-pack download) until the TTL. if ( ! empty( $map ) ) { set_transient( $cache_key, $map, HOUR_IN_SECONDS ); } return $map; } /** * Return translation directory paths and their corresponding URLs. * * Covers both the standard WP languages/plugins/ directory and the * plugin's own bundled languages/ folder. * * @return array */ protected function get_translation_directories(): array { return [ [ 'path' => WP_CONTENT_DIR . '/languages/plugins/', 'url' => content_url( 'languages/plugins/' ), ], [ 'path' => WP_CONTENT_DIR . '/languages/loco/plugins/', 'url' => content_url( 'languages/loco/plugins/' ), ], [ 'path' => aisentic()->plugin_directory . '/languages/', 'url' => aisentic()->plugin_url . '/languages/', ], ]; } }