/** * Convert a string to an array, with utf-8 awareness where possible/required. * * @param string Input * @param boolean Whether to force unicode as on. * @return array Output */ function ocp_mb_str_split($str, $force = false) { $len = ocp_mb_strlen($str, $force); $array = array(); for ($i = 0; $i < $len; $i++) { $array[] = ocp_mb_substr($str, $i, 1, $force); } return $array; }
/** * Get the human-readable form of a language id, or a language entry from a language INI file. * * @param ID_TEXT The language id * @param ?mixed The first token [string or tempcode] (replaces {1}) (NULL: none) * @param ?mixed The second token [string or tempcode] (replaces {2}) (NULL: none) * @param ?mixed The third token (replaces {3}). May be an array of [of string], to allow any number of additional args (NULL: none) * @param ?LANGUAGE_NAME The language to use (NULL: users language) * @param boolean Whether to cause ocPortal to exit if the lookup does not succeed * @return ?mixed The human-readable content (NULL: not found). String normally. Tempcode if tempcode parameters. */ function _do_lang($codename, $token1 = NULL, $token2 = NULL, $token3 = NULL, $lang = NULL, $require_result = true) { $pos = strpos($codename, ':'); if ($pos !== false) { require_lang(substr($codename, 0, $pos)); $codename = substr($codename, $pos + 1); } global $LANGUAGE, $USER_LANG_CACHED, $RECORD_LANG_STRINGS, $XSS_DETECT, $PAGE_CACHE_FILE, $PAGE_CACHE_LANG_LOADED; if ($RECORD_LANG_STRINGS) { global $RECORDED_LANG_STRINGS; $RECORDED_LANG_STRINGS[$codename] = 1; } if ($lang === NULL) { $lang = $USER_LANG_CACHED === NULL ? user_lang() : $USER_LANG_CACHED; } if (!isset($LANGUAGE[$lang][$codename]) && (!array_key_exists($lang, $LANGUAGE) || !array_key_exists($codename, $LANGUAGE[$lang]))) { global $PAGE_CACHE_LAZY_LOAD, $PAGE_CACHE_LANGS_REQUESTED, $LANG_REQUESTED_LANG; if ($PAGE_CACHE_LAZY_LOAD) { $PAGE_CACHE_LAZY_LOAD = false; // We can't be lazy any more, but we will keep growing our pool so hopefully CAN be lazy the next time foreach ($PAGE_CACHE_LANGS_REQUESTED as $request) { list($that_codename, $that_lang) = $request; unset($LANG_REQUESTED_LANG[$that_lang][$that_codename]); require_lang($that_codename, $that_lang, NULL, true); } $ret = _do_lang($codename, $token1, $token2, $token3, $lang, $require_result); if ($ret === NULL) { $PAGE_CACHE_LANG_LOADED[$lang][$codename] = NULL; if ($GLOBALS['MEM_CACHE'] !== NULL) { persistant_cache_set($PAGE_CACHE_FILE, $PAGE_CACHE_LANG_LOADED); } else { @rewind($PAGE_CACHE_FILE); @ftruncate($PAGE_CACHE_FILE, 0); @fwrite($PAGE_CACHE_FILE, serialize($PAGE_CACHE_LANG_LOADED)); } } return $ret; } require_all_open_lang_files($lang); } if ($lang == 'xxx') { return 'xxx'; } // Helpful for testing language compliancy. We don't expect to see non x's if we're running this language if (!isset($LANGUAGE[$lang][$codename]) && (!array_key_exists($lang, $LANGUAGE) || !array_key_exists($codename, $LANGUAGE[$lang]))) { if ($lang != fallback_lang()) { $ret = do_lang($codename, $token1, $token2, $token3, fallback_lang(), $require_result); if ($codename == 'charset') { switch (strtolower($lang)) { case 'ar': case 'bg': case 'zh-CN': case 'zh-TW': case 'hr': case 'cs': case 'da': case 'nl': case 'fi': case 'fr': case 'de': case 'el': case 'hi': case 'it': case 'ja': case 'ko': case 'pl': case 'pt': case 'ro': case 'ru': case 'es': case 'sv': $ret = 'utf-8'; break; } } elseif (substr($codename, 0, 3) == 'FC_') { $ret = ocp_mb_substr(trim(do_lang(substr($codename, 3), $token1, $token2, $token3, $lang)), 0, 1); } elseif ($codename == 'locale') { $ret = strtolower($lang) . '_' . strtoupper($lang); } else { $ret2 = strtolower($codename) != $codename ? google_translate($ret, $lang) : $ret; if ($ret2 != $ret) { $ret = $ret2; } } if ($PAGE_CACHE_FILE !== NULL) { if (!isset($PAGE_CACHE_LANG_LOADED[$lang][$codename]) && isset($PAGE_CACHE_LANG_LOADED[fallback_lang()][$codename])) { $PAGE_CACHE_LANG_LOADED[$lang][$codename] = $ret; // Will have been cached into fallback_lang() from the nested do_lang call, we need to copy it into our cache bucket for this language if ($GLOBALS['MEM_CACHE'] !== NULL) { persistant_cache_set($PAGE_CACHE_FILE, $PAGE_CACHE_LANG_LOADED); } else { @rewind($PAGE_CACHE_FILE); @ftruncate($PAGE_CACHE_FILE, 0); @fwrite($PAGE_CACHE_FILE, serialize($PAGE_CACHE_LANG_LOADED)); } } } return $ret; } else { if ($require_result) { global $USER_LANG_LOOP, $REQUIRE_LANG_LOOP; //print_r(debug_backtrace()); if ($USER_LANG_LOOP == 1) { critical_error('RELAY', 'Missing language code: ' . escape_html($codename) . '. This language code is required to produce error messages, and thus a critical error was prompted by the non-ability to show less-critical error messages. It is likely the source language files (lang/' . fallback_lang() . '/*.ini) for ocPortal on this website have been corrupted.'); } if ($REQUIRE_LANG_LOOP >= 2) { return ''; } // Probably failing to load global.ini, so just output with some text missing require_code('view_modes'); erase_cached_language(); fatal_exit(do_lang_tempcode('MISSING_LANG_ENTRY', escape_html($codename))); } else { return NULL; } } } if ($PAGE_CACHE_FILE !== NULL) { if (!isset($PAGE_CACHE_LANG_LOADED[$lang][$codename])) { $PAGE_CACHE_LANG_LOADED[$lang][$codename] = $LANGUAGE[$lang][$codename]; if ($GLOBALS['MEM_CACHE'] !== NULL) { persistant_cache_set($PAGE_CACHE_FILE, $PAGE_CACHE_LANG_LOADED); } else { @rewind($PAGE_CACHE_FILE); @ftruncate($PAGE_CACHE_FILE, 0); @fwrite($PAGE_CACHE_FILE, serialize($PAGE_CACHE_LANG_LOADED)); } } } // Put in parameters static $non_plural_non_vowel = array('1', 'b', 'c', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'n', 'p', 'q', 'r', 's', 't', 'v', 'w', 'x', 'y', 'z'); $looked_up = $LANGUAGE[$lang][$codename]; if ($looked_up === NULL) { return NULL; } // Learning cache pool has told us this string definitely does not exist $out = str_replace('\\n', "\n", $looked_up); $plural_or_vowel_check = strpos($out, '|') !== false; if ($XSS_DETECT) { ocp_mark_as_escaped($out); } if ($token1 !== NULL) { if (is_object($token1) && $token2 === NULL || $token2 !== NULL && is_object($token2)) { $bits = preg_split('#\\{\\d[^\\}]*\\}#', $out, 2, PREG_SPLIT_OFFSET_CAPTURE); $ret = new ocp_tempcode(); foreach ($bits as $bit) { if ($XSS_DETECT) { ocp_mark_as_escaped($bit[0]); } $at = $bit[1]; if ($at != 0) { if ($out[$at - 2] == '1') { $ret->attach($token1); } elseif ($out[$at - 2] == '2') { $ret->attach($token2); } elseif ($plural_or_vowel_check && substr($out[$at - 2], 0, 2) == '1|') { $exploded = explode('|', $out[$at - 2]); $_token = $token1->evaluate(); $_token_denum = str_replace(',', '', $_token); $ret->attach(in_array(is_numeric($_token_denum) ? $_token_denum : strtolower(substr($_token, 0, 1)), $non_plural_non_vowel) ? $exploded[1] : $exploded[2]); } elseif ($plural_or_vowel_check && substr($out[$at - 2], 0, 2) == '2|') { $exploded = explode('|', $out[$at - 2]); $_token = $token2->evaluate(); $_token_denum = str_replace(',', '', $_token); $ret->attach(in_array(is_numeric($_token_denum) ? $_token_denum : strtolower(substr($_token, 0, 1)), $non_plural_non_vowel) ? $exploded[1] : $exploded[2]); } } $ret->attach($bit[0]); } return $ret; } elseif ($token1 !== NULL) { $out = str_replace('{1}', $token1, $out); if ($plural_or_vowel_check) { $_token_denum = str_replace(',', '', $token1); $out = preg_replace('#\\{1\\|(.*)\\|(.*)\\}#U', in_array(is_numeric($_token_denum) ? $_token_denum : strtolower(substr($token1, 0, 1)), $non_plural_non_vowel) ? '\\1' : '\\2', $out); } if ($XSS_DETECT && ocp_is_escaped($token1)) { ocp_mark_as_escaped($out); } } if ($token2 !== NULL) { if ($XSS_DETECT) { $escaped = ocp_is_escaped($out); } $out = str_replace('{2}', $token2, $out); if ($plural_or_vowel_check) { $_token_denum = str_replace(',', '', $token1); $out = preg_replace('#\\{2\\|(.*)\\|(.*)\\}#U', in_array(is_numeric($_token_denum) ? $_token_denum : strtolower(substr($token2, 0, 1)), $non_plural_non_vowel) ? '\\1' : '\\2', $out); } if ($XSS_DETECT && ocp_is_escaped($token2) && $escaped) { ocp_mark_as_escaped($out); } if ($token3 !== NULL) { $i = 3; if (!is_array($token3)) { $token3 = array($token3); } foreach ($token3 as $token) { if ($XSS_DETECT) { $escaped = ocp_is_escaped($out); } $out = str_replace('{' . strval($i) . '}', $token, $out); if ($plural_or_vowel_check) { $_token_denum = str_replace(',', '', $token); $out = preg_replace('#\\{' . strval($i) . '\\|(.*)\\|(.*)\\}#U', in_array(is_numeric($_token_denum) ? $_token_denum : strtolower(substr($token, 0, 1)), $non_plural_non_vowel) ? '\\1' : '\\2', $out); } if ($XSS_DETECT && ocp_is_escaped($token) && $escaped) { ocp_mark_as_escaped($out); } $i++; } } } } return $out; }
/** * Handle truncation symbols in all their complexity * * @param array Parameters passed to the symbol (0=text, 1=amount, 2=tooltip?, 3=is_html?, 4=use as grammatical length rather than HTML byte length, 5=fractional-deviation-tolerance for grammar-preservation) * @param string The type of truncation to do * @set left right spread * @param ?mixed Tooltip to add on, but only if we end up creating our own tooltip (NULL: none) * @return string The result. */ function symbol_truncator($param, $type, $tooltip_if_truncated = NULL) { $value = ''; if (is_object($param[0])) { $param[0] = $param[0]->evaluate(); if (!isset($param[2])) { $param[2] = '0'; } $param[3] = '1'; } if ($GLOBALS['XSS_DETECT']) { $is_escaped = ocp_is_escaped($param[0]); } $amount = intval(isset($param[1]) ? $param[1] : '60'); $is_html = isset($param[3]) && $param[3] == '1'; if ($is_html) { $not_html = @html_entity_decode(strip_tags($param[0]), ENT_QUOTES, get_charset()); // In case it contains HTML. This is imperfect, but having to cut something up is imperfect from the offset. $html = $param[0]; if ($GLOBALS['XSS_DETECT']) { ocp_mark_as_escaped($html); } if ($html == $not_html && strpos($html, '&') === false && strpos($html, '<') === false) { $is_html = false; } // Conserve memory } else { $not_html = $param[0]; $html = escape_html($param[0]); } if (ocp_mb_strlen($not_html) > $amount) { $tooltip = isset($param[2]) && $param[2] == '1'; $literal_pos = isset($param[4]) ? $param[4] == '1' : false; $grammar_completeness_tolerance = isset($param[5]) ? floatval($param[5]) : 0.0; if ($is_html || $grammar_completeness_tolerance != 0.0) { require_code('xhtml'); } $truncated = $not_html; switch ($type) { case 'left': $temp = $is_html || $grammar_completeness_tolerance != 0.0 ? xhtml_substr($html, 0, $amount - 3, $literal_pos, false, $grammar_completeness_tolerance) : escape_html(ocp_mb_substr($not_html, 0, $amount - 3)); if ($temp != $html && in_array(substr($temp, -1), array('.', '?', '!'))) { $temp .= '<br />'; } // so the "..." does not go right after the sentence terminator $truncated = $temp == $html ? $temp : str_replace(array('</p>…', '</div>…'), array('…</p>', '…</div>'), rtrim($temp) . '…'); break; case 'expand': $temp = $is_html || $grammar_completeness_tolerance != 0.0 ? xhtml_substr($html, 0, $amount - 3, $literal_pos, false, $grammar_completeness_tolerance) : escape_html(ocp_mb_substr($not_html, 0, $amount - 3)); if ($temp != $html && in_array(substr($temp, -1), array('.', '?', '!'))) { $temp .= '<br />'; } // so the "..." does not go right after the sentence terminator $_truncated = do_template('COMCODE_HIDE', array('TEXT' => protect_from_escaping($temp), 'CONTENT' => protect_from_escaping($html))); $truncated = $_truncated->evaluate(); break; case 'right': $truncated = str_replace(array('</p>…', '</div>…'), array('…</p>', '…</div>'), '…' . ltrim($is_html || $grammar_completeness_tolerance != 0.0 ? xhtml_substr($html, -$amount - 3, NULL, $literal_pos, false, $grammar_completeness_tolerance) : escape_html(ocp_mb_substr($not_html, -$amount - 3)))); break; case 'spread': $pos = intval(floor(floatval($amount) / 2.0)) - 1; $truncated = str_replace(array('</p>…', '</div>…'), array('…</p>', '…</div>'), rtrim(($is_html || $grammar_completeness_tolerance != 0.0 ? xhtml_substr($html, 0, $pos, $literal_pos, false, $grammar_completeness_tolerance) : escape_html(ocp_mb_substr($not_html, 0, $pos))) . '…' . ltrim($is_html || $grammar_completeness_tolerance != 0.0 ? xhtml_substr($html, -$pos - 1) : escape_html(ocp_mb_substr($not_html, -$pos - 1))))); break; } if ($tooltip) { if (!is_null($tooltip_if_truncated)) { $tif = is_object($tooltip_if_truncated) ? $tooltip_if_truncated->evaluate() : $tooltip_if_truncated; if (strpos($tif, $html) !== false) { $html = $tif; } else { $html .= ' – ' . $tif; } } $tpl = strpos($truncated, '<div') !== false || strpos($truncated, '<p') !== false || strpos($truncated, '<table') !== false ? 'CROP_TEXT_MOUSE_OVER' : 'CROP_TEXT_MOUSE_OVER_INLINE'; $value_tempcode = do_template($tpl, array('_GUID' => '36ae945ed864633cfa0d67e5c3f2d1c8', 'TEXT_SMALL' => $truncated, 'TEXT_LARGE' => $html)); $value = $value_tempcode->evaluate(); if ($GLOBALS['XSS_DETECT']) { ocp_mark_as_escaped($value); } } else { $value = $truncated; } } else { $value = $html; } if ($GLOBALS['XSS_DETECT']) { if ($is_escaped || !$is_html) { ocp_mark_as_escaped($value); } } return $value; }
/** * Get the human-readable form of a language id, or a language entry from a language INI file. * * @param ID_TEXT The language id * @param ?mixed The first token [string or tempcode] (replaces {1}) (NULL: none) * @param ?mixed The second token [string or tempcode] (replaces {2}) (NULL: none) * @param ?mixed The third token (replaces {3}). May be an array of [of string], to allow any number of additional args (NULL: none) * @param ?LANGUAGE_NAME The language to use (NULL: users language) * @param boolean Whether to cause ocPortal to exit if the lookup does not succeed * @return ?mixed The human-readable content (NULL: not found). String normally. Tempcode if tempcode parameters. */ function _do_lang($codename, $token1 = NULL, $token2 = NULL, $token3 = NULL, $lang = NULL, $require_result = true) { global $LANGUAGE, $USER_LANG_CACHED, $RECORD_LANG_STRINGS, $XSS_DETECT, $PAGE_CACHE_FILE, $PAGE_CACHE_LANG_LOADED; if ($lang === NULL) { $lang = $USER_LANG_CACHED === NULL ? user_lang() : $USER_LANG_CACHED; } if ($GLOBALS['SEMI_DEBUG_MODE']) { $pos = strpos($codename, '='); if ($pos !== false) { // Find loaded file with smallest levenstein distance to current page $best = mixed(); $best_for = NULL; global $LANGS_REQUESTED; foreach (array_keys($LANGS_REQUESTED) as $possible) { $dist = levenshtein(get_page_name(), $possible); if (is_null($best) || $best > $dist) { $best = $dist; $best_for = $possible; } } $save_path = get_file_base() . '/lang/' . fallback_lang() . '/' . $best_for . '.ini'; if (!is_file($save_path)) { $save_path = get_file_base() . '/lang_custom/' . fallback_lang() . '/' . $best_for . '.ini'; } // Tack language strings onto this file list($codename, $value) = explode('=', $codename, 2); $myfile = fopen($save_path, 'at'); fwrite($myfile, "\n" . $codename . '=' . $value); fclose($myfile); // Fake-load the string $LANGUAGE[$lang][$codename] = $value; // Go through all required files, doing a string replace if needed $included_files = get_included_files(); foreach ($included_files as $inc) { $orig_contents = file_get_contents($inc); $contents = str_replace("'" . $codename . '=' . $value . "'", "'" . $codename . "'", $orig_contents); if ($orig_contents != $contents) { $myfile = fopen($inc, 'wt'); fwrite($myfile, $contents); fclose($myfile); } } } } $there = isset($LANGUAGE[$lang][$codename]); if (!$there) { $pos = strpos($codename, ':'); if ($pos !== false) { require_lang(substr($codename, 0, $pos), NULL, NULL, !$require_result); $codename = substr($codename, $pos + 1); } $there = isset($LANGUAGE[$lang][$codename]); } if ($RECORD_LANG_STRINGS) { global $RECORDED_LANG_STRINGS; $RECORDED_LANG_STRINGS[$codename] = 1; } if (!$there && (!isset($LANGUAGE[$lang]) || !array_key_exists($codename, $LANGUAGE[$lang]))) { global $PAGE_CACHE_LAZY_LOAD, $PAGE_CACHE_LANGS_REQUESTED, $LANG_REQUESTED_LANG; if ($PAGE_CACHE_LAZY_LOAD) { $PAGE_CACHE_LAZY_LOAD = false; // We can't be lazy any more, but we will keep growing our pool so hopefully CAN be lazy the next time foreach ($PAGE_CACHE_LANGS_REQUESTED as $request) { list($that_codename, $that_lang) = $request; unset($LANG_REQUESTED_LANG[$that_lang][$that_codename]); require_lang($that_codename, $that_lang, NULL, true); } $ret = _do_lang($codename, $token1, $token2, $token3, $lang, $require_result); if ($ret === NULL) { $PAGE_CACHE_LANG_LOADED[$lang][$codename] = NULL; if ($GLOBALS['MEM_CACHE'] !== NULL) { persistant_cache_set($PAGE_CACHE_FILE, $PAGE_CACHE_LANG_LOADED); } else { open_page_cache_file(); @rewind($PAGE_CACHE_FILE); @flock($PAGE_CACHE_FILE, LOCK_EX); @ftruncate($PAGE_CACHE_FILE, 0); @fwrite($PAGE_CACHE_FILE, serialize($PAGE_CACHE_LANG_LOADED)); @flock($PAGE_CACHE_FILE, LOCK_UN); } } return $ret; } require_all_open_lang_files($lang); } if ($lang == 'xxx') { return 'xxx'; } // Helpful for testing language compliancy. We don't expect to see non x's if we're running this language if (!isset($LANGUAGE[$lang][$codename]) && ($require_result || !isset($LANGUAGE[$lang]) || !array_key_exists($codename, $LANGUAGE[$lang]))) { if ($lang != fallback_lang()) { $ret = do_lang($codename, $token1, $token2, $token3, fallback_lang(), $require_result); if ($PAGE_CACHE_FILE !== NULL) { if (!isset($PAGE_CACHE_LANG_LOADED[$lang][$codename]) && isset($PAGE_CACHE_LANG_LOADED[fallback_lang()][$codename])) { $PAGE_CACHE_LANG_LOADED[$lang][$codename] = $PAGE_CACHE_LANG_LOADED[fallback_lang()][$codename]; // Will have been cached into fallback_lang() from the nested do_lang call, we need to copy it into our cache bucket for this language if ($GLOBALS['MEM_CACHE'] !== NULL) { persistant_cache_set($PAGE_CACHE_FILE, $PAGE_CACHE_LANG_LOADED); } else { open_page_cache_file(); @rewind($PAGE_CACHE_FILE); @flock($PAGE_CACHE_FILE, LOCK_EX); @ftruncate($PAGE_CACHE_FILE, 0); @fwrite($PAGE_CACHE_FILE, serialize($PAGE_CACHE_LANG_LOADED)); @flock($PAGE_CACHE_FILE, LOCK_UN); } } } return $ret; } else { if ($require_result) { global $USER_LANG_LOOP, $REQUIRE_LANG_LOOP; //print_r(debug_backtrace()); if ($USER_LANG_LOOP == 1) { critical_error('RELAY', 'Missing language code: ' . escape_html($codename) . '. This language code is required to produce error messages, and thus a critical error was prompted by the non-ability to show less-critical error messages. It is likely the source language files (lang/' . fallback_lang() . '/*.ini) for ocPortal on this website have been corrupted.'); } if ($REQUIRE_LANG_LOOP >= 2) { return ''; } // Probably failing to load global.ini, so just output with some text missing require_code('view_modes'); erase_cached_language(); require_code('site'); attach_message(do_lang_tempcode('MISSING_LANG_ENTRY', escape_html($codename)), 'warn'); return ''; } else { return NULL; } } } if ($PAGE_CACHE_FILE !== NULL) { if (!isset($PAGE_CACHE_LANG_LOADED[$lang][$codename]) && (!isset($PAGE_CACHE_LANG_LOADED[$lang]) || !array_key_exists($codename, $PAGE_CACHE_LANG_LOADED[$lang]))) { $PAGE_CACHE_LANG_LOADED[$lang][$codename] = $LANGUAGE[$lang][$codename]; if ($GLOBALS['MEM_CACHE'] !== NULL) { persistant_cache_set($PAGE_CACHE_FILE, $PAGE_CACHE_LANG_LOADED); } else { open_page_cache_file(); @rewind($PAGE_CACHE_FILE); @flock($PAGE_CACHE_FILE, LOCK_EX); @ftruncate($PAGE_CACHE_FILE, 0); @fwrite($PAGE_CACHE_FILE, serialize($PAGE_CACHE_LANG_LOADED)); @flock($PAGE_CACHE_FILE, LOCK_UN); } } } // Put in parameters static $non_plural_non_vowel = array('1', 'b', 'c', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'n', 'p', 'q', 'r', 's', 't', 'v', 'w', 'x', 'y', 'z', '{'); $looked_up = $LANGUAGE[$lang][$codename]; if ($looked_up === NULL) { return NULL; // Learning cache pool has told us this string definitely does not exist } $out = str_replace('\\n', "\n", $looked_up); $plural_or_vowel_check = strpos($out, '|') !== false; if ($XSS_DETECT) { ocp_mark_as_escaped($out); } if ($token1 !== NULL) { if (is_object($token1) && $token2 === NULL || $token2 !== NULL && is_object($token2)) { $bits = preg_split('#\\{\\d[^\\}]*\\}#', $out, 2, PREG_SPLIT_OFFSET_CAPTURE); $ret = new ocp_tempcode(); foreach ($bits as $bit) { if ($XSS_DETECT) { ocp_mark_as_escaped($bit[0]); } $at = $bit[1]; if ($at != 0) { if ($out[$at - 2] == '1') { $ret->attach($token1); } elseif ($out[$at - 2] == '2') { $ret->attach($token2); } elseif ($plural_or_vowel_check && substr($out[$at - 2], 0, 2) == '1|') { $exploded = explode('|', $out[$at - 2]); $_token = $token1->evaluate(); $_token_denum = str_replace(',', '', $_token); $ret->attach(in_array(is_numeric($_token_denum) ? $_token_denum : ocp_mb_strtolower(ocp_mb_substr($_token, 0, 1)), $non_plural_non_vowel) ? $exploded[1] : $exploded[2]); } elseif ($plural_or_vowel_check && substr($out[$at - 2], 0, 2) == '2|') { $exploded = explode('|', $out[$at - 2]); $_token = $token2->evaluate(); $_token_denum = str_replace(',', '', $_token); $ret->attach(in_array(is_numeric($_token_denum) ? $_token_denum : ocp_mb_strtolower(ocp_mb_substr($_token, 0, 1)), $non_plural_non_vowel) ? $exploded[1] : $exploded[2]); } } $ret->attach($bit[0]); } return $ret; } elseif ($token1 !== NULL) { $out = str_replace('{1}', $token1, $out); if ($plural_or_vowel_check) { $_token_denum = str_replace(',', '', $token1); $out = preg_replace('#\\{1\\|(.*)\\|(.*)\\}#U', in_array(is_numeric($_token_denum) ? $_token_denum : ocp_mb_strtolower(ocp_mb_substr($token1, 0, 1)), $non_plural_non_vowel) ? '\\1' : '\\2', $out); } if ($XSS_DETECT && ocp_is_escaped($token1)) { ocp_mark_as_escaped($out); } } if ($token2 !== NULL) { if ($XSS_DETECT) { $escaped = ocp_is_escaped($out); } $out = str_replace('{2}', $token2, $out); if ($plural_or_vowel_check) { $_token_denum = str_replace(',', '', $token1); $out = preg_replace('#\\{2\\|(.*)\\|(.*)\\}#U', in_array(is_numeric($_token_denum) ? $_token_denum : ocp_mb_strtolower(ocp_mb_substr($token2, 0, 1)), $non_plural_non_vowel) ? '\\1' : '\\2', $out); } if ($XSS_DETECT && ocp_is_escaped($token2) && $escaped) { ocp_mark_as_escaped($out); } if ($token3 !== NULL) { $i = 3; if (!is_array($token3)) { $token3 = array($token3); } foreach ($token3 as $token) { if ($XSS_DETECT) { $escaped = ocp_is_escaped($out); } $out = str_replace('{' . strval($i) . '}', $token, $out); if ($plural_or_vowel_check) { $_token_denum = str_replace(',', '', $token); $out = preg_replace('#\\{' . strval($i) . '\\|(.*)\\|(.*)\\}#U', in_array(is_numeric($_token_denum) ? $_token_denum : ocp_mb_strtolower(ocp_mb_substr($token, 0, 1)), $non_plural_non_vowel) ? '\\1' : '\\2', $out); } if ($XSS_DETECT && ocp_is_escaped($token) && $escaped) { ocp_mark_as_escaped($out); } $i++; } } } } return $out; }
/** * Load up a language file, compiling it (it's not cached yet). * * @param ID_TEXT The language file name * @param ?LANGUAGE_NAME The language (NULL: uses the current language) * @param ?string The language type (lang_custom, or custom) (NULL: normal priorities are used) * @set lang_custom custom * @param PATH Where we are cacheing too * @param boolean Whether to just return if there was a loading error * @return boolean Whether we FAILED to load */ function require_lang_compile($codename, $lang, $type, $cache_path, $ignore_errors = false) { global $LANGUAGE, $REQUIRE_LANG_LOOP, $LANG_LOADED_LANG; $desire_cache = function_exists('get_option') && (get_option('is_on_lang_cache', true) == '1' || get_param_integer('keep_cache', 0) == 1 || get_param_integer('cache', 0) == 1) && get_param_integer('keep_cache', NULL) !== 0 && get_param_integer('cache', NULL) !== 0; if ($desire_cache) { if ($GLOBALS['IN_MINIKERNEL_VERSION'] == 0) { global $DECACHED_COMCODE_LANG_STRINGS; // Cleanup language strings if (!$DECACHED_COMCODE_LANG_STRINGS) { $DECACHED_COMCODE_LANG_STRINGS = true; $comcode_lang_strings = $GLOBALS['SITE_DB']->query_select('cached_comcode_pages', array('string_index'), array('the_zone' => '!'), '', NULL, NULL, true); if (!is_null($comcode_lang_strings)) { $GLOBALS['SITE_DB']->query_delete('cached_comcode_pages', array('the_zone' => '!')); foreach ($comcode_lang_strings as $comcode_lang_string) { delete_lang($comcode_lang_string['string_index']); } } } } $load_target = array(); } else { $load_target =& $LANGUAGE[$lang]; } global $FILE_ARRAY; if (@is_array($FILE_ARRAY) && file_array_exists('lang/' . $lang . '/' . $codename . '.ini')) { $lang_file = 'lang/' . $lang . '/' . $codename . '.ini'; $file = file_array_get($lang_file); _get_lang_file_map($file, $load_target, NULL, true); $bad = true; } else { $bad = true; $dirty = false; // Load originals $lang_file = get_file_base() . '/lang/' . $lang . '/' . filter_naughty($codename) . '.ini'; if (file_exists($lang_file)) { _get_lang_file_map($lang_file, $load_target, NULL, false); $bad = false; } // Load overrides now if they are there if ($type != 'lang') { $lang_file = get_custom_file_base() . '/lang_custom/' . $lang . '/' . $codename . '.ini'; if (!file_exists($lang_file) && get_file_base() != get_custom_file_base()) { $lang_file = get_file_base() . '/lang_custom/' . $lang . '/' . $codename . '.ini'; } if (!file_exists($lang_file)) { $lang_file = get_custom_file_base() . '/lang_custom/' . $lang . '/' . $codename . '.po'; if (!file_exists($lang_file)) { $lang_file = get_file_base() . '/lang_custom/' . $lang . '/' . $codename . '-' . strtolower($lang) . '.po'; } } } if ($type != 'lang' && file_exists($lang_file)) { _get_lang_file_map($lang_file, $load_target, NULL, false); $bad = false; $dirty = true; // Tainted from the official pack, so can't store server wide } // NB: Merge op doesn't happen in require_lang. It happens when do_lang fails and then decides it has to force a recursion to do_lang(xx,fallback_lang()) which triggers require_lang(xx,fallback_lang()) when it sees it's not loaded if ($bad && $lang != fallback_lang()) { require_lang($codename, fallback_lang(), $type, $ignore_errors); $REQUIRE_LANG_LOOP--; $fallback_cache_path = get_custom_file_base() . '/lang_cached/' . fallback_lang() . '/' . $codename . '.lcd'; if (file_exists($fallback_cache_path)) { require_code('files'); $fallback_map = unserialize(file_get_contents($fallback_cache_path)); $sep = '<span class="notranslate">----</span>'; $to_translate = ''; $i = 0; $from = 0; $lang_codes = array_keys($fallback_map); foreach ($fallback_map as $value) { if (strlen($to_translate . $sep . $to_translate) >= 3000) { $translated = preg_split('#<span class="notranslate">[^<>]*----[^<>]*</span>#', google_translate($to_translate, $lang)); foreach ($translated as $j => $t_value) { if (strtolower($lang_codes[$from + $j]) == $lang_codes[$from + $j]) { $t_value = $fallback_map[$lang_codes[$from + $j]]; } if ($lang_codes[$from + $j] == 'locale') { $t_value = strtolower($lang) . '_' . strtoupper($lang); } $fallback_map[$lang_codes[$from + $j]] = $t_value; $load_target[$lang_codes[$from + $j]] = $t_value; } $from = $i; $to_translate = ''; } if ($to_translate != '') { $to_translate .= $sep; } $to_translate .= $value; $i++; } $translated = preg_split('#<span class="notranslate">[^<>]*----[^<>]*</span>#', google_translate($to_translate, $lang)); foreach ($translated as $j => $t_value) { if (strtolower($lang_codes[$from + $j]) == $lang_codes[$from + $j]) { $t_value = $fallback_map[$lang_codes[$from + $j]]; } if ($lang_codes[$from + $j] == 'locale') { $t_value = strtolower($lang) . '_' . strtoupper($lang); } $fallback_map[$lang_codes[$from + $j]] = $t_value; $load_target[$lang_codes[$from + $j]] = $t_value; } if (function_exists('ocp_mb_substr') && $codename == 'dates') { foreach (array_keys($fallback_map) as $key) { if (substr($key, 0, 3) == 'FC_') { $test = ocp_mb_substr(trim($fallback_map[substr($key, 3)]), 0, 1, true); if ($test !== false) { $fallback_map[$key] = $test; } } } } $myfile = fopen($cache_path, 'wb'); fwrite($myfile, serialize($fallback_map)); fclose($myfile); fix_permissions($cache_path); } if (!array_key_exists($lang, $LANG_LOADED_LANG)) { $LANG_LOADED_LANG[$lang] = array(); } $LANG_LOADED_LANG[$lang][$codename] = 1; if (!$bad) { $LANGUAGE[$lang] += $fallback_map; } return $bad; } if ($bad) { if ($ignore_errors) { return true; } if ($codename != 'critical_error' || $lang != get_site_default_lang()) { fatal_exit(do_lang_tempcode('MISSING_LANG_FILE', escape_html($codename), escape_html($lang))); } else { critical_error('CRIT_LANG'); } } } if (is_null($GLOBALS['MEM_CACHE'])) { // Cache if ($desire_cache) { $file = @fopen($cache_path, 'wt'); // Will fail if cache dir missing .. e.g. in quick installer if ($file) { if (fwrite($file, serialize($load_target)) > 0) { // Success fclose($file); require_code('files'); fix_permissions($cache_path); } else { // Failure fclose($file); @unlink($cache_path); } } } } else { persistant_cache_set(array('LANG', $lang, $codename), $load_target, !$dirty); } if ($desire_cache) { $LANGUAGE[$lang] += $load_target; } return $bad; }