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));
+        }
+    }
+}