Browse Source

Localization class, and other improvements

Language file format will almost certainly change
microsub
J. King 8 years ago
parent
commit
db0bd18663
  1. 1
      bootstrap.php
  2. 8
      locale/en.php
  3. 10
      vendor/JKingWeb/NewsSync/Auth/AuthInterface.php
  4. 25
      vendor/JKingWeb/NewsSync/Auth/DriverInternal.php
  5. 32
      vendor/JKingWeb/NewsSync/Conf.php
  6. 6
      vendor/JKingWeb/NewsSync/Conf/Exception.php
  7. 32
      vendor/JKingWeb/NewsSync/Exception.php
  8. 126
      vendor/JKingWeb/NewsSync/Lang.php
  9. 6
      vendor/JKingWeb/NewsSync/Lang/Exception.php
  10. 16
      vendor/JKingWeb/NewsSync/RuntimeData.php

1
bootstrap.php

@ -3,6 +3,7 @@ declare(strict_types=1);
namespace JKingWeb\NewsSync;
const BASE = __DIR__.DIRECTORY_SEPARATOR;
const NS_BASE = __NAMESPACE__."\\";
spl_autoload_register(function ($class) {
if($class=="SimplePie") return;

8
locale/en.php

@ -0,0 +1,8 @@
<?php
$string[] = [
"Exception.JKingWeb/NewsSync/Lang/Exception.defaultFileMissing" => "Default language file \"{0}\" missing",
"Exception.JKingWeb/NewsSync/Lang/Exception.fileMissing" => "Language file \"{0}\" is not available",
"Exception.JKingWeb/NewsSync/Lang/Exception.fileUnreadable" => "Insufficient permissions to read language file \"{0}\"",
"Exception.JKingWeb/NewsSync/Lang/Exception.fileCorrupt" => "Language file \"{0}\" is corrupt or does not conform to expected format",
"Exception.JKingWeb/NewsSync/Lang/Exception.stringMissing" => "Message string \"{msgID}\" missing from all loaded language files ({fileList})",
];

10
vendor/JKingWeb/NewsSync/Auth/AuthInterface.php

@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace JKingWeb\NewsSync\Auth;
Interface AuthInterface {
public function __construct($conf, $db);
public function auth(): bool;
public function authHTTP(): bool;
public function isAdmin(): bool;
}

25
vendor/JKingWeb/NewsSync/Auth/DriverInternal.php

@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace JKingWeb\NewsSync\Auth;
use JKingWeb\NewsSync;
class Internal implements AuthInterface {
protected $conf;
protected $db;
public function __construct($conf, $db) {
}
public function auth(): bool {
}
public function authHTTP(): bool {
}
public function isAdmin(): bool {
}
}

32
vendor/JKingWeb/NewsSync/Conf.php

@ -3,7 +3,9 @@ declare(strict_types=1);
namespace JKingWeb\NewsSync;
class Conf {
public $dbType = "SQLite3";
public $lang = "en";
public $dbClass = NS_BASE."Db\\DriverSQLite3";
public $dbSQLite3PDO = false;
public $dbSQLite3File = BASE."newssync.db";
public $dbPostgreSQLPDO = false;
@ -20,29 +22,41 @@ class Conf {
public $dbMySQLPort = 3306;
public $dbMySQLDb = "newssync";
public $authClass = NS_BASE."Auth\\DriverInternal";
public $authPreferHTTP = false;
public $authProvision = false;
public $simplepieCache = BASE.".cache";
function __construct(string $import_file = "") {
if($import_file != "") $this->import($import_file);
public function __construct(string $import_file = "") {
if($import_file != "") $this->import_file($import_file);
}
function import(string $file): bool {
public function importFile(string $file): self {
if(!file_exists($file)) throw new Conf\Exception("fileMissing");
if(!is_readable($file)) throw new Conf\Exception("fileUnreadable");
$json = @file_get_contents($file);
if($json===false) return false;
if($json===false) throw new Conf\Exception("fileUnreadable");
return $this->import($json);
}
public function import(string $json): self {
if($json=="") throw new Conf\Exception("blank");
$json = json_decode($json, true);
if(!is_array(json)) return false;
if(!is_array($json)) throw new Conf\Exception("corrupt");
foreach($json as $key => $value) {
$this->$$key = $value;
}
return true;
return $this;
}
function export(string $file = ""): string {
public function export(string $file = ""): string {
return json_encode($this, JSON_PRETTY_PRINT);
}
function __toString(): string {
public function __toString(): string {
return $this->export();
}
}

6
vendor/JKingWeb/NewsSync/Conf/Exception.php

@ -0,0 +1,6 @@
<?php
declare(strict_types=1);
namespace JKingWeb\NewsSync\Conf;
class Exception extends \JKingWeb\NewsSync\Exception {
}

32
vendor/JKingWeb/NewsSync/Exception.php

@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace JKingWeb\NewsSync;
class Exception extends \Exception {
const CODES = [
"Exception.Misc" => 10000,
"Lang/Exception.defaultFileMissing" => 10101,
"Lang/Exception.fileMissing" => 10102,
"Lang/Exception.fileUnreadable" => 10103,
"Lang/Exception.fileCorrupt" => 10104,
"Lang/Exception.stringMissing" => 10105,
];
public function __construct(string $msgID = "", $vars = null, Throwable $e = null) {
if($msgID=="") {
$msg = "";
$code = 0;
} else {
$msg = "Exception.".str_replace("\\","/",get_called_class()).".$msgID";
$msg = Lang::msg($msg, $vars);
$codeID = str_replace("\\", "/", str_replace(NS_BASE, "", get_called_class()));
if(!array_key_exists($codeID,self::CODES)) {
$code = 0;
} else {
$code = self::CODES[$codeID];
}
}
parent::__construct($msg, $code, $e);
}
}

126
vendor/JKingWeb/NewsSync/Lang.php

@ -0,0 +1,126 @@
<?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;
}
}

6
vendor/JKingWeb/NewsSync/Lang/Exception.php

@ -0,0 +1,6 @@
<?php
declare(strict_types=1);
namespace JKingWeb\NewsSync\Lang;
class Exception extends \JKingWeb\NewsSync\Exception {
}

16
vendor/JKingWeb/NewsSync/RuntimeData.php

@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace JKingWeb\NewsSync;
class RuntimeData {
protected $conf;
protected $db;
protected $auth;
public function __construct(Conf $conf) {
Lang::set();
$this->conf = $conf;
//$this->db = new Database($this);
//$this->auth = new Authenticator($this);
}
}
Loading…
Cancel
Save