<?php

namespace Illuminate\Translation;

use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Illuminate\Support\Collection;
use Illuminate\Support\NamespacedItemResolver;
use Symfony\Component\Translation\MessageSelector;
use Symfony\Component\Translation\TranslatorInterface;
// a namespace about this Translator.php
class Translator extends NamespacedItemResolver implements TranslatorInterface
{// Translator extends Namespace Item Resolver implements Translator Interface
    /**
     * The loader implementation.
     *
     * @var \Illuminate\Translation\LoaderInterface
     */
    protected $loader;//The loader implementation.

    /**
     * The default locale being used by the translator.
     *
     * @var string
     */
    protected $locale;//The default locale being used by the translator.

    /**
     * The fallback locale used by the translator.
     *
     * @var string
     */
    protected $fallback;//The fallback locale used by the translator.

    /**
     * The array of loaded translation groups.
     *
     * @var array
     */
    protected $loaded = [];// The array of loaded translation groups.

    /**
     * The message selector.
     * 
     * @var \Symfony\Component\Translation\MessageSelector
     */
    protected $selector;// The message selector .

    /**
     * Create a new translator instance.
     *
     * @param  \Illuminate\Translation\LoaderInterface  $loader
     * @param  string  $locale
     * @return void
     */
    public function __construct(LoaderInterface $loader, $locale)
    {
        $this->loader = $loader;// a set
        $this->locale = $locale;// set loader and locale
    }//Create a new translator instance.

    /**
     * Determine if a translation exists for a given locale.
     *
     * @param  string  $key
     * @param  string|null  $locale
     * @return bool
     */
    public function hasForLocale($key, $locale = null)
    {
        return $this->has($key, $locale, false);//has determine has this locale
    }//Determine if a translation exists for a given locale.

    /**
     * Determine if a translation exists.
     *
     * @param  string  $key
     * @param  string|null  $locale
     * @param  bool  $fallback
     * @return bool
     */
    public function has($key, $locale = null, $fallback = true)
    {
        return $this->get($key, [], $locale, $fallback) !== $key;
    }// a very popular function,
   // determine something is has

    /**
     * Get the translation for the given key.
     *
     * @param  string  $key
     * @param  array   $replace
     * @param  string|null  $locale
     * @param  bool  $fallback
     * @return string|array|null
     */
    public function get($key, array $replace = [], $locale = null, $fallback = true)
    {//Get the translation for the given key.
        list($namespace, $group, $item) = $this->parseKey($key);//this is a good type

        // Here we will get the locale that should be used for the language line. If one
        // was not passed, we will use the default locales which was given to us when
        // the translator was instantiated. Then, we can load the lines and return.
        $locales = $fallback ? $this->parseLocale($locale) : [$locale ?: $this->locale];
       // get the default locales

        foreach ($locales as $locale) {// loop locales
            $this->load($namespace, $group, $locale);// load file

            $line = $this->getLine(// get line
                $namespace, $group, $locale, $item, $replace
            );

            if (! is_null($line)) {// if it not null
                break;
            }
        }// to stop something

        // If the line doesn't exist, we will return back the key which was requested as
        // that will be quick to spot in the UI if language keys are wrong or missing
        // from the application's language files. Otherwise we can return the line.
        if (! isset($line)) {// if no line we will return key
            return $key;
        }

        return $line;// default return line
    }

    /**
     * Retrieve a language line out the loaded array.
     *
     * @param  string  $namespace
     * @param  string  $group
     * @param  string  $locale
     * @param  string  $item
     * @param  array   $replace
     * @return string|array|null
     */
    protected function getLine($namespace, $group, $locale, $item, array $replace)
    {//retrieve like search,
       //Retrieve a language line out the loaded array.
        $line = Arr::get($this->loaded[$namespace][$group][$locale], $item);//get the default line
// a wrap function is very ok.
        if (is_string($line)) {// string
            return $this->makeReplacements($line, $replace);
        } elseif (is_array($line) && count($line) > 0) {// a set array [not null]
            return $line;
        }
    }

    /**
     * Make the place-holder replacements on a line.
     *
     * @param  string  $line
     * @param  array   $replace
     * @return string
     */
    protected function makeReplacements($line, array $replace)
    {//Make the place-holder replacements on a line
        $replace = $this->sortReplacements($replace);// get this replace and sort Replacements

        foreach ($replace as $key => $value) {// loop
            $line = str_replace(
                [':'.Str::upper($key), ':'.Str::ucfirst($key), ':'.$key],
                [Str::upper($value), Str::ucfirst($value), $value],
                $line
            );//replace ,this  i like to use array map
        }

        return $line;
    }// which better

    /**
     * Sort the replacements array.
     *
     * @param  array  $replace
     * @return array
     */
    protected function sortReplacements(array $replace)
    {
        return (new Collection($replace))->sortBy(function ($value, $key) {
            return mb_strlen($key) * -1;
        });// a instance of the Collection
       // too powerful
    }//Sort the repalcements array

    /**
     * Get a translation according to an integer value.
     *
     * @param  string  $key
     * @param  int|array|\Countable  $number
     * @param  array   $replace
     * @param  string  $locale
     * @return string
     */
    public function choice($key, $number, array $replace = [], $locale = null)
    {//Get a translation according to an integer value.
        $line = $this->get($key, $replace, $locale = $locale ?: $this->locale ?: $this->fallback);
//first get this line
        if (is_array($number) || $number instanceof \Countable) {
            $number = count($number);
        }// second get the count amazing function

        $replace['count'] = $number;//set the config method

        return $this->makeReplacements($this->getSelector()->choose($line, $number, $locale), $replace);
    }//return $this make Repalcements

    /**
     * Get the translation for a given key.
     *
     * @param  string  $id
     * @param  array   $parameters
     * @param  string  $domain
     * @param  string  $locale
     * @return string|array|null
     */
    public function trans($id, array $parameters = [], $domain = 'messages', $locale = null)
    {
        return $this->get($id, $parameters, $locale);// trans just another get
       // then has some fixed parameters
    }//Get the translation for a given key.

    /**
     * Get a translation according to an integer value.
     *
     * @param  string  $id
     * @param  int|array|\Countable  $number
     * @param  array   $parameters
     * @param  string  $domain
     * @param  string  $locale
     * @return string
     */
    public function transChoice($id, $number, array $parameters = [], $domain = 'messages', $locale = null)
    {// Choice like a big wrap
        return $this->choice($id, $number, $parameters, $locale);
    }// Get a translation according to an integer value.

    /**
     * Load the specified language group.
     *
     * @param  string  $namespace
     * @param  string  $group
     * @param  string  $locale
     * @return void
     */
    public function load($namespace, $group, $locale)
    {//Load the specified language group.
        if ($this->isLoaded($namespace, $group, $locale)) {
            return;
        }// first check it , if ok ,just return null ,

        // The loader is responsible for returning the array of language lines for the
        // given namespace, group, and locale. We'll set the lines in this array of
        // lines that have already been loaded so that we can easily access them.
        $lines = $this->loader->load($locale, $group, $namespace);// load it

        $this->loaded[$namespace][$group][$locale] = $lines;//set this load flag.
    }

    /**
     * Determine if the given group has been loaded.
     *
     * @param  string  $namespace
     * @param  string  $group
     * @param  string  $locale
     * @return bool
     */
    protected function isLoaded($namespace, $group, $locale)
    {// isLoaded just to check this array ,if has, then will return itself
        return isset($this->loaded[$namespace][$group][$locale]);
    }//Determine if the given group has been loaded.

    /**
     * Add a new namespace to the loader.
     *
     * @param  string  $namespace
     * @param  string  $hint
     * @return void
     */
    public function addNamespace($namespace, $hint)
    {
        $this->loader->addNamespace($namespace, $hint);
    }// use loader to addNamespace

    /**
     * Parse a key into namespace, group, and item.
     *
     * @param  string  $key
     * @return array
     */
    public function parseKey($key)
    {//Parse a key into namespace,group ,and item.
        $segments = parent::parseKey($key);// set segments parent::parseKey

        if (is_null($segments[0])) {//if it is null
            $segments[0] = '*';// set default
        }//difference style.
       // i like this type
       //$segments[0] = ?:$segments[0];

        return $segments;//return it self.
    }

    /**
     * Get the array of locales to be checked.
     *
     * @param  string|null  $locale
     * @return array
     */
    protected function parseLocale($locale)
    {
        if (! is_null($locale)) {
            return array_filter([$locale, $this->fallback]);
        }// is_null
       // return array_filter

        return array_filter([$this->locale, $this->fallback]);
    }//Get the array of locales to be checked.

    /**
     * Get the message selector instance.
     *
     * @return \Symfony\Component\Translation\MessageSelector
     */
    public function getSelector()
    {
        if (! isset($this->selector)) {
            $this->selector = new MessageSelector;// set it
        }// isset(this->selector)

        return $this->selector;// selector
    }//Get the message selector instance.

    /**
     * Set the message selector instance.
     *
     * @param  \Symfony\Component\Translation\MessageSelector  $selector
     * @return void
     */
    public function setSelector(MessageSelector $selector)
    {
        $this->selector = $selector;// big set
    }// Set the message selector instance.

    /**
     * Get the language line loader implementation.
     *
     * @return \Illuminate\Translation\LoaderInterface
     */
    public function getLoader()
    {
        return $this->loader;
    }// Get the language line loader implementation.

    /**
     * Get the default locale being used.
     *
     * @return string
     */
    public function locale()
    {
        return $this->getLocale();
    }// get Locale

    /**
     * Get the default locale being used.
     *
     * @return string
     */
    public function getLocale()
    {
        return $this->locale;
    }// big return locale

    /**
     * Set the default locale.
     *
     * @param  string  $locale
     * @return void
     */
    public function setLocale($locale)
    {
        $this->locale = $locale;
    }// big set Locale

    /**
     * Get the fallback locale being used.
     *
     * @return string
     */
    public function getFallback()
    {
        return $this->fallback;
    }// big get

    /**
     * Set the fallback locale being used.
     *
     * @param  string  $fallback
     * @return void
     */
    public function setFallback($fallback)
    {
        $this->fallback = $fallback;
    }// big set
}