locale = get_locale(); } /** * Function: load * Loads translations from the .mo file into the supplied domain. * * Parameters: * $domain - The name of this translation domain. * $path - The path to the locale directory. * $reload - Reload the translation if already loaded? */ public function load($domain, $path, $reload = false): bool { $filepath = $path.DIR.$this->locale.DIR."LC_MESSAGES".DIR.$domain.".mo"; if (isset($this->mo[$domain]) and !$reload) return true; if (!is_file($filepath) or !is_readable($filepath)) return false; $mo_file = file_get_contents($filepath); $mo_data = array(); $mo_length = strlen($mo_file); $big_endian = null; if (self::MO_SIZEOF_HEADER > $mo_length) return false; $id = unpack("H8magic", $mo_file); if ($id["magic"] == self::MO_MAGIC_WORD_BE) $big_endian = true; if ($id["magic"] == self::MO_MAGIC_WORD_LE) $big_endian = false; # Neither magic word matches; not a valid .mo file. if (!isset($big_endian)) return false; $unpack = ($big_endian) ? "Nformat/Nnum/Nor/Ntr" : "Vformat/Vnum/Vor/Vtr" ; $mo_offset = unpack($unpack, $mo_file, 4); $unpack = ($big_endian) ? "Nlength/Noffset" : "Vlength/Voffset" ; for ($i = 0; $i < $mo_offset["num"]; $i++) { $or_str_offset = $mo_offset["or"] + ($i * 8); $tr_str_offset = $mo_offset["tr"] + ($i * 8); if (($or_str_offset + 8) > $mo_length) return false; if (($tr_str_offset + 8) > $mo_length) return false; $or_str_meta = unpack($unpack, $mo_file, $or_str_offset); $tr_str_meta = unpack($unpack, $mo_file, $tr_str_offset); $or_str_end = $or_str_meta["offset"] + $or_str_meta["length"]; $tr_str_end = $tr_str_meta["offset"] + $tr_str_meta["length"]; if ($or_str_end > $mo_length) return false; if ($tr_str_end > $mo_length) return false; $or_str_data = substr( $mo_file, $or_str_meta["offset"], $or_str_meta["length"] ); $tr_str_data = substr( $mo_file, $tr_str_meta["offset"], $tr_str_meta["length"] ); # Discover null-separated plural forms. $or_str_data = explode("\0", $or_str_data); $tr_str_data = explode("\0", $tr_str_data); # Add discovered messages to the data. $mo_data[] = array( "or" => $or_str_data, "tr" => $tr_str_data ); } $this->mo[$domain] = $mo_data; return true; } /** * Function: text * Returns the singular or plural translation of a string. * * Parameters: * $domain - The translation domain to search. * $single - Singular string. * $plural - Pluralized string (optional). * $number - The number to judge by (optional). */ public function text($domain, $single, $plural = null, $number = 1): string { if (isset($plural)) { $array = $this->find($domain, $plural); $n = (int) $number; $nplural = $this->nplural($n); return fallback( $array[$nplural], ($n != 1) ? $plural : $single ); } $array = $this->find($domain, $single); return fallback($array[0], $single); } /** * Function: find * Returns a translation array from the supplied domain. */ public function find($domain, $string): array { if (!isset($this->mo[$domain])) return array(); foreach ($this->mo[$domain] as $entry) { if (in_array($string, $entry["or"], true)) return $entry["tr"]; } return array(); } /** * Function: nplural * Support for for languages with n != 2 plural forms. */ private function nplural($n): int { static $base; if (!isset($base)) $base = strtolower(lang_base($this->locale)); switch ($base) { case "zh": return 0; case "ar": if ($n == 0) return 0; elseif ($n == 1) return 1; elseif ($n == 2) return 2; elseif ($n % 100 >= 3 and $n % 100 <= 10) return 3; elseif ($n % 100 >= 11 and $n % 100 <= 99) return 4; else return 5; default: return ($n != 1) ? 1 : 0 ; } } /** * Function: current * Returns a singleton reference to the current class. */ public static function & current(): self { static $instance = null; $instance = (empty($instance)) ? new self() : $instance ; return $instance; } }