From a58efcdef4fbac667f4bc971ca7c83c6fbc63a69 Mon Sep 17 00:00:00 2001 From: Dave Mc Nicoll <info@mcnd.ca> Date: Sat, 21 Dec 2019 13:51:56 -0500 Subject: [PATCH] - First commit, some small work porting an old lib I had done into this first draft --- composer.json | 18 +++++++ src/I18n.php | 106 +++++++++++++++++++++++++++++++++++++++++ src/Iterate.php | 117 ++++++++++++++++++++++++++++++++++++++++++++++ src/TellTrait.php | 47 +++++++++++++++++++ 4 files changed, 288 insertions(+) create mode 100644 composer.json create mode 100644 src/I18n.php create mode 100644 src/Iterate.php create mode 100644 src/TellTrait.php diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..b7c41f0 --- /dev/null +++ b/composer.json @@ -0,0 +1,18 @@ +{ + "name": "mcnd/tell", + "description": "JSON-based i18n library.", + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "Dave Mc Nicoll", + "email": "mcndave@gmail.com" + } + ], + "require": {}, + "autoload": { + "psr-4": { + "Tell\\": "src/" + } + } +} diff --git a/src/I18n.php b/src/I18n.php new file mode 100644 index 0000000..ebf191a --- /dev/null +++ b/src/I18n.php @@ -0,0 +1,106 @@ +<?php + +namespace Tell; + +class I18n +{ + const DELIMITER = '.'; + + protected bool $updatedData = false; + + protected array $data = []; + + protected array $cache = []; + + protected array $engine = []; + + protected string $currentLanguage = ""; + + protected array $fileStack = []; + + protected function __construct(bool $enableCache = false) { + +# $this->persistent = new Persistent(static::class); + if ( $enableCache ) { +# $data = $this->persistent->load(); +# $this->data = $data['data']; +# $this->fileStack = $data['fileStack']; + } + } + + public function __destruct() { +# $this->update_data() && $this->persistent->save([ +# 'fileStack' => $this->fileStack, +# 'data' => $this->data +# ]); + } + + public function updateData() { + return $this->updatedData; + } + + public static function get(?string $key = null) { + if ( $key === null ) { + return $this->data; + } + + return isset( $this->cache[$key] ) ? $this->cache[$key] : $this->cache[$key] = Iterate::arrayGet($this->data, ( $this->currentLanguage ? $this->currentLanguage."." : "").$key, static::DELIMITER); + } + + public static function set(string $key, $value) { + $cache = explode(static::DELIMITER, $key); + + while ( !empty($cache) ) { + $imp = implode(static::DELIMITER, $cache); + array_pop($cache); + unset( $this->cache[$imp] ); + } + + $this->cache[$key] = $value ; + + return Iterate::arraySet($this->data, $key, $value, static::DELIMITER); + } + + public function addEngine($engine) { + $this->engine[] = $engine; + } + + public function load(string $path, bool $filenameAsKey = false) { + if ( !in_array($path, $this->fileStack) ) { + $this->updatedData = true; + $this->fileStack[] = $path; + + foreach(Iterate::files($path) as $item) { + foreach($this->engine as $engine) { + if ( $engine->accept($item )) { + + if ( $array = $engine->translate( $item ) ){ + if ($filenameAsKey ) { + $base = basename($item); + + $key = substr($base,0, strrpos($base, ".")); + $keysplit = explode(".", $key); + $key = array_pop($keysplit); + + $this->mergeData([ $keysplit ? "{".implode(".", array_merge([ $key ], $keysplit))."}" : $key => $array ]); + } + else { + $this->mergeData($array); + } + } + + continue 2; + } + } + } + } + } + + public static function currentLanguage($set = null) { + return $set === null ? $this->currentLanguage : $this->currentLanguage = $set; + } + + protected function mergeData($data) { + $this->data = array_replace_recursive($this->data, Iterate::splitKeys($data)); + } +} diff --git a/src/Iterate.php b/src/Iterate.php new file mode 100644 index 0000000..7dc5be9 --- /dev/null +++ b/src/Iterate.php @@ -0,0 +1,117 @@ +<?php + +namespace Tell; + +use RecursiveDirectoryIterator, + RecursiveIteratorIterator, + RecursiveRegexIterator, + RegexIterator; + +class Iterate +{ + /** + * Set value to array with a given path using recursion + * @param array $array - array to set passed by reference ! + * @param string $path - path like 'person.name.first' + * @param string $value - value to set + */ + public static function arraySet(array &$array, string $path, string $value = '', string $delimiter = '.') { + $pathArr = explode($delimiter, $path); + + // Go to next node + if ( isset($pathArr[1])) { + static::arraySet($array[ array_shift($pathArr) ], implode($delimiter, $pathArr), $value); + } + // We are at the end of the path, set value + else { + $array[ $pathArr[0] ] = $value; + } + } + + public static function arrayGet(array $array, string $path, string $delimiter = '.') { + $pathArr = explode($delimiter, $path); + + if (isset($array[$pathArr[0]])) { + if ( isset($pathArr[1]) ) { + return static::arrayGet($array[array_shift($pathArr)], implode($delimiter, $pathArr)); + } + else { + return $array[$pathArr[0]]; + } + } + else { + return null; + } + } + + public static function splitKeys(array &$array, string $delimiter = ".", string $enclosed = '{}') { + if ( is_array($array) ) { + $swap = []; + + foreach($array as $key => &$value) { + $process = strstr($key, $delimiter); + + if ( $enclosed ) { + if ( ( strlen($key) - 2 ) !== strlen( $tkey = trim($key, $enclosed) ) ) { + $process = false; + } + else { + $oldKey = $key; + $key = $tkey; + } + } + + if ( $process ) { + $tmp = []; + $keylist = explode($delimiter, $key); + $finalKey = array_shift($keylist); + # Arrayobj::iterate_set($keylist, $tmp, $value); + static::arraySet($tmp, $key, $value); + $swap[$finalKey] = static::splitKeys($tmp, $delimiter); + + unset($array[isset($oldKey) ? $oldKey : $key]); + } + else if ( is_array($value) ) { + $value = static::splitKeys($value, $delimiter); + } + } + + foreach($swap as $key => $item) { + if ( $enclosed ) { + if ( ( strlen($key) - 2 ) === ( $tkey = trim($key, $enclosed)) ) { + $key = $tkey; + } + } + + $array[$key] = $item; + } + } + + return $array; + } + + public static function files(string $path, string $fileExtension = "") { + $retval = []; + + if ( \file_exists($path) ) { + $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::SELF_FIRST, RecursiveIteratorIterator::CATCH_GET_CHILD); + + if ($fileExtension) { + $iterator = new RegexIterator($iterator, '/^.+\.'.$fileExtension.'$/i', RecursiveRegexIterator::GET_MATCH); + } + + foreach ($iterator as $file) { + if ($fileExtension) { + $retval[] = $file[0]; + } + else { + if ( $file->isFile() || $file->isDir() ) { + $retval[] = $file->getRealPath(); + } + } + } + } + return $retval; + } + +} diff --git a/src/TellTrait.php b/src/TellTrait.php new file mode 100644 index 0000000..5e90cda --- /dev/null +++ b/src/TellTrait.php @@ -0,0 +1,47 @@ +<?php + +namespace Tell; + +trait TellTrait { + + public function lang(...$args) + { + static $vars_pattern = null; + + $vars_pattern || ( $vars_pattern = $this->config('Eckinox.language.vars.pattern') ); + + switch ( count($args) ) { + case 1: + return ( $value = Language::get( $args[0] ) ) !== null ? $value : ( Eckinox::debug() ? "'{$args[0]}'" : "" ); + + case 0: + return Language::instance(); + + case 2: + if ( empty($args[1]) ) { + return $this->lang($args[0]); + } + + /* Handling arrays with keys */ + if ( Arrayobj::array_is_associative($args[1]) ) { + $content = $this->lang($args[0]); + + if ( preg_match_all("~\\{\\$(.*?)\\}~si", $this->lang($args[0]), $matches, PREG_SET_ORDER) ) { + $search = []; + + foreach($matches as $item) { + $search[ $item[0] ] = $a = iterate::array_get($args[1], $item[1]); + } + + $content = str_replace(array_keys($search), array_values($search), $content); + } + + return $content; + } + + default: + /* Handling arrays with indexes */ + return call_user_func_array('sprintf', array_merge([ $this->lang( array_shift($args) ) ], $args)); + } + } +}