The clean & modern RSS server that doesn't give you any crap. https://thearsse.com/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

126 lines
4.7 KiB

<?php
declare(strict_types=1);
namespace JKingWeb\NewsSync;
class Lang {
const PATH = BASE."locale".DIRECTORY_SEPARATOR;
const DEFAULT = "en";
const REQUIRED = [
"Exception.JKingWeb/NewsSync/Lang/Exception.defaultFileMissing" => "Default language file ({0}) missing",
"Exception.JKingWeb/NewsSync/Lang/Exception.stringMissing" => "Message string \"{msgID}\" missing from all loaded language files ({fileList})"
];
static protected $requirementsMet = false;
static protected $synched = false;
static protected $wanted = "fr";
static protected $locale = "";
static protected $loaded = [];
static protected $strings = self::REQUIRED;
protected function __construct() {}
static public function set(string $locale = "", bool $immediate = false): string {
if(!self::$requirementsMet) self::checkRequirements();
if($locale=="") $locale = self::DEFAULT;
if($locale==self::$wanted) return $locale;
$list = self::listFiles();
if(!in_array(self::DEFAULT, $list)) throw new Lang\Exception("defaultFileMissing", self::DEFAULT);
self::$wanted = self::match($locale, $list);
self::$synched = false;
if($immediate) self::load();
return self::$wanted;
}
static public function get(): string {
return (self::$locale=="") ? self::DEFAULT : self::$locale;
}
static public function msg(string $msgID, $vars = null): string {
// if we're trying to load the system default language and it fails, we have a chicken and egg problem, so we catch the exception and load no language file instead
if(!self::$synched) try {self::load();} catch(Lang\Exception $e) {
if(self::$wanted==self::DEFAULT) {
self::set();
self::load();
} else {
throw $e;
}
}
if(!array_key_exists($msgID, self::$strings)) throw new Lang\Exception("stringMissing", ['msgID' => $msgID, 'fileList' => implode(", ",self::$loaded)]);
// variables fed to MessageFormatter must be contained in array
if($vars !== null && !is_array($vars)) $vars = [$vars];
return \MessageFormatter::formatMessage(self::$locale, self::$strings[$msgID], $vars);
}
static public function list(string $locale = ""): array {
$out = [];
$files = self::listFiles();
foreach($files as $tag) {
$out[$tag] = \Locale::getDisplayName($tag, ($locale=="") ? $tag : $locale);
}
return $out;
}
static public function match(string $locale, array $list = null): string {
if($list===null) $list = self::listFiles();
$default = (self::$locale=="") ? self::DEFAULT : self::$locale;
return \Locale::lookup($list,$locale, true, $default);
}
static protected function checkRequirements(): bool {
if(!extension_loaded("intl")) throw new FatalException("The \"Intl\" extension is required, but not loaded");
self::$requirementsMet = true;
return true;
}
static protected function listFiles(string $path = self::PATH): array {
$out = glob($path."*.php");
return array_map(function($file) {
$file = substr($file,strrpos($file,DIRECTORY_SEPARATOR)+1);
return strtolower(substr($file,0,strrpos($file,".")));
},$out);
}
static protected function load(): bool {
self::$synched = true;
if(!self::$requirementsMet) self::checkRequirements();
// if we've yet to request a locale, just load the fallback strings and return
if(self::$wanted=="") {
self::$strings = self::REQUIRED;
self::$locale = self::$wanted;
return true;
}
// decompose the requested locale from specific to general, building a list of files to load
$tags = \Locale::parseLocale(self::$wanted);
$files = [];
$loaded = [];
$strings = [];
while(sizeof($tags) > 0) {
$files[] = \Locale::composeLocale($tags);
$tag = array_pop($tags);
}
// include the default locale as the base if the most general locale requested is not the default
if($tag != self::DEFAULT) $files[] = self::DEFAULT;
// save the list of files to be loaded for later reference
$loaded = $files;
// reduce the list of files to be loaded to the minimum necessary (e.g. if we go from "fr" to "fr_ca", we don't need to load "fr" or "en")
$files = [];
foreach($loaded as $file) {
if($file==self::$locale) break;
$files[] = $file;
}
// if we need to load all files, start with the fallback strings
if($files==$loaded) $strings[] = self::REQUIRED;
// read files in reverse order
$files = array_reverse($files);
foreach($files as $file) {
if(!file_exists(self::PATH."$file.php")) throw new Lang\Exception("fileMissing", $file);
if(!is_readable(self::PATH."$file.php")) throw new Lang\Exception("fileUnreadable", $file);
if(!@include(self::PATH."$file.php")) throw new Lang\Exception("fileCorrupt", $file);
}
// apply the results and return
self::$strings = call_user_func_array("array_replace_recursive", $strings);
self::$loaded = $loaded;
self::$locale = self::$wanted;
return true;
}
}