(.*?)<\/div>$/si', '
$2', $frag_value, 1);
}
if ($fragment['format'] === 'html-main') {
$tag = 'linguise-main';
}
if ($fragment['format'] === 'media-img' || $fragment['format'] === 'media-imgset') {
$tag = 'img';
}
$frag_format = $fragment['format'];
if (isset($fragment['skip']) && $fragment['skip']) {
$frag_format .= '-skip';
}
$html .= '<' . $tag . ' class="linguise-fragment" data-fragment-name="' . $fragment_name . '" data-fragment-param="' . $fragment_param . '" data-fragment-key="';
$html .= $fragment['key'] . '" data-fragment-format="' . $frag_format . '" data-fragment-mode="' . $json_fragments['mode'] . '"';
if ($json_fragments['mode'] === 'attribute' && isset($json_fragments['attribute'])) {
$html .= ' data-fragment-extra-id="' . $json_fragments['attribute'] . '"';
}
if ($json_fragments['mode'] === 'i18n' && isset($fragment['index'])) {
$html .= ' data-fragment-extra-id="' . $fragment['index'] . '"';
}
if ($fragment['format'] === 'link') {
$html .= ' href="' . $fragment['value'] . '">';
} elseif ($fragment['format'] === 'media-img' || $fragment['format'] === 'media-imgset') {
$tag_select = $fragment['format'] === 'media-imgset' ? 'srcset' : 'src';
$html .= ' ' . $tag_select . '="' . $fragment['value'] . '">';
} else {
$html .= '>' . self::protectSprintfTemplates($frag_value);
}
$html .= '' . $tag . '>' . "\n";
}
return trim($html) . "\n";
}
/**
* Convert back the translated fragments into the original JSON data
*
* @param string $html_fragments The HTML fragments that was injected into the page
*
* @return array - The array of fragments, generated with collectFragmentFromJson
*/
public static function intoJSONFragments($html_fragments)
{
$fragments = [];
// Let's just use regex to get anything with "linguise-fragment" class
preg_match_all(self::$frag_html_match, $html_fragments, $std_matches, PREG_SET_ORDER, 0);
preg_match_all(self::$frag_html_match_self_close, $html_fragments, $self_close_matches, PREG_SET_ORDER, 0);
$matches = array_merge($std_matches, $self_close_matches);
foreach ($matches as $match) {
$fragment_name = $match[2];
$fragment_param = $match[3];
$fragment_key = $match[4];
$fragment_format = $match[5];
$fragment_mode = $match[6];
$fragment_value = isset($match[9]) ? $match[9] : '';
$is_skipped = false;
if (substr($fragment_format, -5) === '-skip') {
$is_skipped = true;
$fragment_format = substr($fragment_format, 0, -5);
}
if ($fragment_format === 'link' || $fragment_format === 'media-img' || $fragment_format === 'media-imgset') {
$fragment_value = $match[8];
}
if ($fragment_format === 'html') {
// parse back the linguise-dev
$fragment_value = preg_replace('/
(.*?)<\/linguise-div>$/si', '$2
', $fragment_value, 1);
} elseif ($fragment_format === 'html-main') {
// parse back the linguise-main
$fragment_value = preg_replace('/(.*?)<\/linguise-main>$/si', '$1', $fragment_value, 1);
}
// Strip any data-editor="linguise" wrap
$result_strip = preg_replace('/^\s*(.*?)<\/span>$/', '$1', $fragment_value, 1);
if (!empty($result_strip)) {
$fragment_value = $result_strip;
}
$fragment_value = self::restoreSprintfTemplates($fragment_value);
if (!isset($fragments[$fragment_name])) {
$fragments[$fragment_name] = [];
}
if (!isset($fragments[$fragment_name][$fragment_param])) {
$temp_fragment_sets = [
'mode' => $fragment_mode,
'fragments' => [],
];
if ($fragment_mode === 'attribute' && isset($match[7]) && !empty($match[7])) {
$temp_fragment_sets['attribute'] = $match[7];
}
$fragments[$fragment_name][$fragment_param] = $temp_fragment_sets;
}
// The returned data is encoded HTML entities for non-ASCII characters
// Decode it back to UTF-8 for link and text, for HTML since it would be rendered
// the browser will decode it back to UTF-8 automatically
if ($fragment_format !== 'html' && $fragment_format !== 'html-main') {
$fragment_value = html_entity_decode($fragment_value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}
$fragment_index = null;
if ($fragment_mode === 'i18n' && isset($match[7]) && is_numeric($match[7])) {
$fragment_index = (int)$match[7];
}
// make it into list for each fragment name
$fragments[$fragment_name][$fragment_param]['fragments'][] = [
'key' => $fragment_key,
'value' => $fragment_value,
'format' => $fragment_format,
'match' => $match[0],
'index' => $fragment_index,
'skip' => $is_skipped, // If `skip` is true, then don't replace this fragment (but remove it)
];
}
Debug::log('FragmentHandler -> Dump fragments: ' . json_encode($fragments, JSON_PRETTY_PRINT));
return $fragments;
}
/**
* === HTML Injector Related ===
*/
/**
* Try to match the script with the override list.
*
* @param \DOMNode|\DOMElement $script The script to be matched
* @param string $html_data The raw HTML data to be checked
*
* @return array|null
*/
private static function tryMatchWithOverride($script, $html_data)
{
return FragmentOverrideHandler::tryMatchWithOverride($script, $html_data);
}
/**
* Parse the translation block from the script tag.
*
* @param \DOMNode|\DOMElement $script The script to be matched
*
* @return array|null
*/
public static function tryMatchTranslationBlock($script)
{
return FragmentI18nHandler::tryMatchTranslationBlock($script);
}
/**
* Parse the HTML input or data into the fragments.
* XXX: Maybe normal regex and not DOMDocument if possible.
*
* @param string $html_data The HTML data to be parsed
*
* @return array
*/
public static function findWPFragments(&$html_data)
{
$html_dom = HTMLHelper::loadHTML($html_data);
if (empty($html_dom)) {
return [];
}
$scripts = $html_dom->getElementsByTagName('script');
$all_fragments = [];
foreach ($scripts as $script) {
$attr_id = $script->getAttribute('id');
$match_res = preg_match('/^(.*)-js-extra$/', $attr_id, $attr_match);
if ($match_res === false || $match_res === 0) {
$overridden_temp = self::tryMatchWithOverride($script, $html_data);
if (is_array($overridden_temp)) {
foreach ($overridden_temp as $overridden_temp_item) {
$all_fragments[$overridden_temp_item['name']][$overridden_temp_item['name']] = [
'mode' => 'override',
'fragments' => $overridden_temp_item['fragments'],
];
}
continue;
}
// Try matching -js-translations
$trans_match_res = preg_match('/^(.*)-js-translations$/', $attr_id, $trans_attr_match);
if ($trans_match_res !== false && $trans_match_res !== 0) {
$translation_block = self::tryMatchTranslationBlock($script);
$param_name = $trans_attr_match[1];
if (is_array($translation_block)) {
$all_fragments[$param_name][$translation_block['name']] = [
'mode' => 'i18n',
'fragments' => $translation_block['fragments'],
];
}
}
continue;
}
$frag_id = $attr_match[1];
$script_content = HTMLHelper::unclobberCdataInternal($script->textContent);
$match_res = preg_match('/var ' . preg_quote(str_replace('-', '_', $frag_id), '/') . '_params = (.*);/', $script_content, $json_matches);
if ($match_res === false || $match_res === 0) {
$unmatched_res = preg_match_all('/var (.+)_params = (.*);/', $script_content, $json_multi_matches, PREG_SET_ORDER, 0);
if ($unmatched_res === false || $unmatched_res === 0) {
$overridden_temp = self::tryMatchWithOverride($script, $html_data);
if (is_array($overridden_temp)) {
foreach ($overridden_temp as $overridden_temp_item) {
$all_fragments[$frag_id][$overridden_temp_item['name']] = [
'mode' => 'override',
'fragments' => $overridden_temp_item['fragments'],
];
}
}
continue;
}
// Since this is might have multiple matches, we need to check each of them
foreach ($json_multi_matches as $json_multi_match) {
$json_par_key = $json_multi_match[1];
$json_data = $json_multi_match[2];
$collected_temp = self::collectFragmentFromJson(json_decode($json_data, true));
if (!empty($collected_temp)) {
$all_fragments[$frag_id][$json_par_key] = [
'mode' => 'auto',
'fragments' => $collected_temp,
];
}
}
continue;
}
$json_data = $json_matches[1];
$collected_temp = self::collectFragmentFromJson(json_decode($json_data, true));
if (!empty($collected_temp)) {
$all_fragments[$frag_id][str_replace('-', '_', $frag_id)] = [
'mode' => 'auto',
'fragments' => $collected_temp,
];
}
}
// Run filters
$filtered_fragments = $all_fragments;
$filtered_fragments = apply_filters('linguise_after_fragment_collection', $filtered_fragments, $html_data, $html_dom);
if (is_array($filtered_fragments)) {
$all_fragments = $filtered_fragments;
}
Debug::log('FragmentHandler -> Collected: ' . json_encode($all_fragments, JSON_PRETTY_PRINT));
return $all_fragments;
}
/**
* Apply the translated fragments for the override.
*
* @param string $html_data The HTML data to be injected
* @param string $fragment_name The name of the fragment, e.g. 'woocommerce'
* @param string $fragment_param The param of the fragment, e.g. 'woocommerce$$attr-1'
* @param array $fragment_info The array of fragments to be injected, from intoJSONFragments
*
* @return string
*/
public static function applyTranslatedFragmentsForOverride($html_data, $fragment_name, $fragment_param, $fragment_info)
{
return FragmentOverrideHandler::applyTranslatedFragmentsForOverride($html_data, $fragment_name, $fragment_param, $fragment_info);
}
/**
* Apply the translated fragments for the auto mode.
*
* @param array $json_data The JSON data to be injected
* @param array $fragments The array of fragments to be injected, from intoJSONFragments
*
* @return array
*/
public static function applyTranslatedFragmentsForAuto($json_data, $fragments)
{
return FragmentAutoHandler::applyTranslatedFragmentsForAuto($json_data, $fragments);
}
/**
* Apply the translated fragments for the i18n mode.
*
* @param string $html_data The HTML data to be injected
* @param string $param_name The name of the fragment, e.g. 'woocommerce'
* @param string $param_key The param of the fragment, e.g. 'messages'
* @param array $fragments The array of fragments to be injected, from intoJSON
*
* @return string
*/
public static function applyTranslatedFragmentsForI18n($html_data, $param_name, $param_key, $fragments)
{
return FragmentI18nHandler::applyTranslatedFragmentsForI18n($html_data, $param_name, $param_key, $fragments);
}
/**
* Inject the fragments into the HTML data.
*
* @param string $html_data The HTML data to be injected
* @param array $fragments The array of fragments to be injected, from intoJSONFragments
*
* @return string
*/
public static function injectTranslatedWPFragments($html_data, $fragments)
{
Debug::log('FragmentHandler -> Injecting: ' . json_encode($fragments, JSON_PRETTY_PRINT));
// Remove the `linguise-fragment` marker elements for every group we're about to process here,
// directly in the DOM. This is done once, up front, so it's unaffected by the string-based
// substitutions below (see FragmentBase::removeFragmentMarkers() for why this can't be a
// literal str_replace() against the raw captured HTML).
$html_dom = null;
foreach ($fragments as $fragment_name => $fragment_jsons) {
foreach ($fragment_jsons as $fragment_param => $fragment_list) {
if (!in_array($fragment_list['mode'], ['auto', 'override', 'skip', 'i18n'])) {
continue;
}
if ($html_dom === null) {
$html_dom = HTMLHelper::loadHTML($html_data);
if (empty($html_dom)) {
break 2;
}
}
self::removeFragmentMarkers($html_dom, $fragment_name, $fragment_param);
}
}
if (!empty($html_dom)) {
$html_data = HTMLHelper::saveHTML($html_dom);
}
foreach ($fragments as $fragment_name => $fragment_jsons) {
foreach ($fragment_jsons as $fragment_param => $fragment_list) {
$mode = $fragment_list['mode'];
if (!in_array($mode, ['auto', 'override', 'skip', 'i18n'])) {
continue;
}
if ($mode === 'skip') {
continue;
}
if ($mode === 'override') {
$html_data = self::applyTranslatedFragmentsForOverride($html_data, $fragment_param, $fragment_name, $fragment_list);
continue;
}
if ($mode === 'i18n') {
// i18n mode for translation blocks
$html_data = self::applyTranslatedFragmentsForI18n($html_data, $fragment_name, $fragment_param, $fragment_list['fragments']);
continue;
}
$id = preg_quote($fragment_name . '-js-extra', '/');
$variable = preg_quote($fragment_param . '_params', '/');
$match_ptrn = '/';
$replacement = preg_replace($match_ptrn, $subst_ptrn, $html_data, 1, $count);
if ($count) {
$html_data = $replacement;
}
}
}
$mod_html_data = apply_filters('linguise_after_fragment_translation', $html_data, $fragments);
if (!empty($mod_html_data)) {
$html_data = $mod_html_data;
}
return $html_data;
}
}