8889841cHasLocalePreference.php000066600000000336150513040670011116 0ustar00loader = $loader; $this->setLocale($locale); } /** * 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); } /** * 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; } /** * Get the translation for the given key. * * @param string $key * @param array $replace * @param string|null $locale * @param bool $fallback * @return string|array */ public function get($key, array $replace = [], $locale = null, $fallback = true) { $locale = $locale ?: $this->locale; // For JSON translations, there is only one file per locale, so we will simply load // that file and then we will be ready to check the array for the key. These are // only one level deep so we do not need to do any fancy searching through it. $this->load('*', '*', $locale); $line = $this->loaded['*']['*'][$locale][$key] ?? null; // If we can't find a translation for the JSON key, we will attempt to translate it // using the typical translation file. This way developers can always just use a // helper such as __ instead of having to pick between trans or __ with views. if (! isset($line)) { [$namespace, $group, $item] = $this->parseKey($key); // 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->localeArray($locale) : [$locale]; foreach ($locales as $locale) { if (! is_null($line = $this->getLine( $namespace, $group, $locale, $item, $replace ))) { return $line; } } } // 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. return $this->makeReplacements($line ?: $key, $replace); } /** * Get a translation according to an integer value. * * @param string $key * @param \Countable|int|array $number * @param array $replace * @param string|null $locale * @return string */ public function choice($key, $number, array $replace = [], $locale = null) { $line = $this->get( $key, $replace, $locale = $this->localeForChoice($locale) ); // If the given "number" is actually an array or countable we will simply count the // number of elements in an instance. This allows developers to pass an array of // items without having to count it on their end first which gives bad syntax. if (is_array($number) || $number instanceof Countable) { $number = count($number); } $replace['count'] = $number; return $this->makeReplacements( $this->getSelector()->choose($line, $number, $locale), $replace ); } /** * Get the proper locale for a choice operation. * * @param string|null $locale * @return string */ protected function localeForChoice($locale) { return $locale ?: $this->locale ?: $this->fallback; } /** * 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) { $this->load($namespace, $group, $locale); $line = Arr::get($this->loaded[$namespace][$group][$locale], $item); if (is_string($line)) { return $this->makeReplacements($line, $replace); } elseif (is_array($line) && count($line) > 0) { foreach ($line as $key => $value) { $line[$key] = $this->makeReplacements($value, $replace); } return $line; } } /** * Make the place-holder replacements on a line. * * @param string $line * @param array $replace * @return string */ protected function makeReplacements($line, array $replace) { if (empty($replace)) { return $line; } $shouldReplace = []; foreach ($replace as $key => $value) { $shouldReplace[':'.Str::ucfirst($key)] = Str::ucfirst($value); $shouldReplace[':'.Str::upper($key)] = Str::upper($value); $shouldReplace[':'.$key] = $value; } return strtr($line, $shouldReplace); } /** * Add translation lines to the given locale. * * @param array $lines * @param string $locale * @param string $namespace * @return void */ public function addLines(array $lines, $locale, $namespace = '*') { foreach ($lines as $key => $value) { [$group, $item] = explode('.', $key, 2); Arr::set($this->loaded, "$namespace.$group.$locale.$item", $value); } } /** * Load the specified language group. * * @param string $namespace * @param string $group * @param string $locale * @return void */ public function load($namespace, $group, $locale) { if ($this->isLoaded($namespace, $group, $locale)) { return; } // 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); $this->loaded[$namespace][$group][$locale] = $lines; } /** * 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) { return isset($this->loaded[$namespace][$group][$locale]); } /** * 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); } /** * Add a new JSON path to the loader. * * @param string $path * @return void */ public function addJsonPath($path) { $this->loader->addJsonPath($path); } /** * Parse a key into namespace, group, and item. * * @param string $key * @return array */ public function parseKey($key) { $segments = parent::parseKey($key); if (is_null($segments[0])) { $segments[0] = '*'; } return $segments; } /** * Get the array of locales to be checked. * * @param string|null $locale * @return array */ protected function localeArray($locale) { return array_filter([$locale ?: $this->locale, $this->fallback]); } /** * Get the message selector instance. * * @return \Illuminate\Translation\MessageSelector */ public function getSelector() { if (! isset($this->selector)) { $this->selector = new MessageSelector; } return $this->selector; } /** * Set the message selector instance. * * @param \Illuminate\Translation\MessageSelector $selector * @return void */ public function setSelector(MessageSelector $selector) { $this->selector = $selector; } /** * Get the language line loader implementation. * * @return \Illuminate\Contracts\Translation\Loader */ public function getLoader() { return $this->loader; } /** * Get the default locale being used. * * @return string */ public function locale() { return $this->getLocale(); } /** * Get the default locale being used. * * @return string */ public function getLocale() { return $this->locale; } /** * Set the default locale. * * @param string $locale * @return void * * @throws \InvalidArgumentException */ public function setLocale($locale) { if (Str::contains($locale, ['/', '\\'])) { throw new InvalidArgumentException('Invalid characters present in locale.'); } $this->locale = $locale; } /** * Get the fallback locale being used. * * @return string */ public function getFallback() { return $this->fallback; } /** * Set the fallback locale being used. * * @param string $fallback * @return void */ public function setFallback($fallback) { $this->fallback = $fallback; } /** * Set the loaded translation groups. * * @param array $loaded * @return void */ public function setLoaded(array $loaded) { $this->loaded = $loaded; } } LICENSE.md000064400000002063150553623110006152 0ustar00The MIT License (MIT) Copyright (c) Taylor Otwell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. TranslationServiceProvider.php000064400000002662150553623110012616 0ustar00registerLoader(); $this->app->singleton('translator', function ($app) { $loader = $app['translation.loader']; // When registering the translator component, we'll need to set the default // locale as well as the fallback locale. So, we'll grab the application // configuration so we can easily get both of these values from there. $locale = $app['config']['app.locale']; $trans = new Translator($loader, $locale); $trans->setFallback($app['config']['app.fallback_locale']); return $trans; }); } /** * Register the translation line loader. * * @return void */ protected function registerLoader() { $this->app->singleton('translation.loader', function ($app) { return new FileLoader($app['files'], $app['path.lang']); }); } /** * Get the services provided by the provider. * * @return array */ public function provides() { return ['translator', 'translation.loader']; } } FileLoader.php000064400000011157150553623110007271 0ustar00path = $path; $this->files = $files; } /** * Load the messages for the given locale. * * @param string $locale * @param string $group * @param string|null $namespace * @return array */ public function load($locale, $group, $namespace = null) { if ($group === '*' && $namespace === '*') { return $this->loadJsonPaths($locale); } if (is_null($namespace) || $namespace === '*') { return $this->loadPath($this->path, $locale, $group); } return $this->loadNamespaced($locale, $group, $namespace); } /** * Load a namespaced translation group. * * @param string $locale * @param string $group * @param string $namespace * @return array */ protected function loadNamespaced($locale, $group, $namespace) { if (isset($this->hints[$namespace])) { $lines = $this->loadPath($this->hints[$namespace], $locale, $group); return $this->loadNamespaceOverrides($lines, $locale, $group, $namespace); } return []; } /** * Load a local namespaced translation group for overrides. * * @param array $lines * @param string $locale * @param string $group * @param string $namespace * @return array */ protected function loadNamespaceOverrides(array $lines, $locale, $group, $namespace) { $file = "{$this->path}/vendor/{$namespace}/{$locale}/{$group}.php"; if ($this->files->exists($file)) { return array_replace_recursive($lines, $this->files->getRequire($file)); } return $lines; } /** * Load a locale from a given path. * * @param string $path * @param string $locale * @param string $group * @return array */ protected function loadPath($path, $locale, $group) { if ($this->files->exists($full = "{$path}/{$locale}/{$group}.php")) { return $this->files->getRequire($full); } return []; } /** * Load a locale from the given JSON file path. * * @param string $locale * @return array * * @throws \RuntimeException */ protected function loadJsonPaths($locale) { return collect(array_merge($this->jsonPaths, [$this->path])) ->reduce(function ($output, $path) use ($locale) { if ($this->files->exists($full = "{$path}/{$locale}.json")) { $decoded = json_decode($this->files->get($full), true); if (is_null($decoded) || json_last_error() !== JSON_ERROR_NONE) { throw new RuntimeException("Translation file [{$full}] contains an invalid JSON structure."); } $output = array_merge($output, $decoded); } return $output; }, []); } /** * Add a new namespace to the loader. * * @param string $namespace * @param string $hint * @return void */ public function addNamespace($namespace, $hint) { $this->hints[$namespace] = $hint; } /** * Get an array of all the registered namespaces. * * @return array */ public function namespaces() { return $this->hints; } /** * Add a new JSON path to the loader. * * @param string $path * @return void */ public function addJsonPath($path) { $this->jsonPaths[] = $path; } /** * Get an array of all the registered paths to JSON translation files. * * @return array */ public function jsonPaths() { return $this->jsonPaths; } } composer.json000064400000001727150553623110007276 0ustar00{ "name": "illuminate/translation", "description": "The Illuminate Translation package.", "license": "MIT", "homepage": "https://laravel.com", "support": { "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, "authors": [ { "name": "Taylor Otwell", "email": "taylor@laravel.com" } ], "require": { "php": "^7.3|^8.0", "ext-json": "*", "illuminate/collections": "^8.0", "illuminate/contracts": "^8.0", "illuminate/macroable": "^8.0", "illuminate/filesystem": "^8.0", "illuminate/support": "^8.0" }, "autoload": { "psr-4": { "Illuminate\\Translation\\": "" } }, "extra": { "branch-alias": { "dev-master": "8.x-dev" } }, "config": { "sort-packages": true }, "minimum-stability": "dev" } MessageSelector.php000064400000026676150553623110010364 0ustar00extract($segments, $number)) !== null) { return trim($value); } $segments = $this->stripConditions($segments); $pluralIndex = $this->getPluralIndex($locale, $number); if (count($segments) === 1 || ! isset($segments[$pluralIndex])) { return $segments[0]; } return $segments[$pluralIndex]; } /** * Extract a translation string using inline conditions. * * @param array $segments * @param int $number * @return mixed */ private function extract($segments, $number) { foreach ($segments as $part) { if (! is_null($line = $this->extractFromString($part, $number))) { return $line; } } } /** * Get the translation string if the condition matches. * * @param string $part * @param int $number * @return mixed */ private function extractFromString($part, $number) { preg_match('/^[\{\[]([^\[\]\{\}]*)[\}\]](.*)/s', $part, $matches); if (count($matches) !== 3) { return null; } $condition = $matches[1]; $value = $matches[2]; if (Str::contains($condition, ',')) { [$from, $to] = explode(',', $condition, 2); if ($to === '*' && $number >= $from) { return $value; } elseif ($from === '*' && $number <= $to) { return $value; } elseif ($number >= $from && $number <= $to) { return $value; } } return $condition == $number ? $value : null; } /** * Strip the inline conditions from each segment, just leaving the text. * * @param array $segments * @return array */ private function stripConditions($segments) { return collect($segments)->map(function ($part) { return preg_replace('/^[\{\[]([^\[\]\{\}]*)[\}\]]/', '', $part); })->all(); } /** * Get the index to use for pluralization. * * The plural rules are derived from code of the Zend Framework (2010-09-25), which * is subject to the new BSD license (https://framework.zend.com/license) * Copyright (c) 2005-2010 - Zend Technologies USA Inc. (http://www.zend.com) * * @param string $locale * @param int $number * @return int */ public function getPluralIndex($locale, $number) { switch ($locale) { case 'az': case 'az_AZ': case 'bo': case 'bo_CN': case 'bo_IN': case 'dz': case 'dz_BT': case 'id': case 'id_ID': case 'ja': case 'ja_JP': case 'jv': case 'ka': case 'ka_GE': case 'km': case 'km_KH': case 'kn': case 'kn_IN': case 'ko': case 'ko_KR': case 'ms': case 'ms_MY': case 'th': case 'th_TH': case 'tr': case 'tr_CY': case 'tr_TR': case 'vi': case 'vi_VN': case 'zh': case 'zh_CN': case 'zh_HK': case 'zh_SG': case 'zh_TW': return 0; case 'af': case 'af_ZA': case 'bn': case 'bn_BD': case 'bn_IN': case 'bg': case 'bg_BG': case 'ca': case 'ca_AD': case 'ca_ES': case 'ca_FR': case 'ca_IT': case 'da': case 'da_DK': case 'de': case 'de_AT': case 'de_BE': case 'de_CH': case 'de_DE': case 'de_LI': case 'de_LU': case 'el': case 'el_CY': case 'el_GR': case 'en': case 'en_AG': case 'en_AU': case 'en_BW': case 'en_CA': case 'en_DK': case 'en_GB': case 'en_HK': case 'en_IE': case 'en_IN': case 'en_NG': case 'en_NZ': case 'en_PH': case 'en_SG': case 'en_US': case 'en_ZA': case 'en_ZM': case 'en_ZW': case 'eo': case 'eo_US': case 'es': case 'es_AR': case 'es_BO': case 'es_CL': case 'es_CO': case 'es_CR': case 'es_CU': case 'es_DO': case 'es_EC': case 'es_ES': case 'es_GT': case 'es_HN': case 'es_MX': case 'es_NI': case 'es_PA': case 'es_PE': case 'es_PR': case 'es_PY': case 'es_SV': case 'es_US': case 'es_UY': case 'es_VE': case 'et': case 'et_EE': case 'eu': case 'eu_ES': case 'eu_FR': case 'fa': case 'fa_IR': case 'fi': case 'fi_FI': case 'fo': case 'fo_FO': case 'fur': case 'fur_IT': case 'fy': case 'fy_DE': case 'fy_NL': case 'gl': case 'gl_ES': case 'gu': case 'gu_IN': case 'ha': case 'ha_NG': case 'he': case 'he_IL': case 'hu': case 'hu_HU': case 'is': case 'is_IS': case 'it': case 'it_CH': case 'it_IT': case 'ku': case 'ku_TR': case 'lb': case 'lb_LU': case 'ml': case 'ml_IN': case 'mn': case 'mn_MN': case 'mr': case 'mr_IN': case 'nah': case 'nb': case 'nb_NO': case 'ne': case 'ne_NP': case 'nl': case 'nl_AW': case 'nl_BE': case 'nl_NL': case 'nn': case 'nn_NO': case 'no': case 'om': case 'om_ET': case 'om_KE': case 'or': case 'or_IN': case 'pa': case 'pa_IN': case 'pa_PK': case 'pap': case 'pap_AN': case 'pap_AW': case 'pap_CW': case 'ps': case 'ps_AF': case 'pt': case 'pt_BR': case 'pt_PT': case 'so': case 'so_DJ': case 'so_ET': case 'so_KE': case 'so_SO': case 'sq': case 'sq_AL': case 'sq_MK': case 'sv': case 'sv_FI': case 'sv_SE': case 'sw': case 'sw_KE': case 'sw_TZ': case 'ta': case 'ta_IN': case 'ta_LK': case 'te': case 'te_IN': case 'tk': case 'tk_TM': case 'ur': case 'ur_IN': case 'ur_PK': case 'zu': case 'zu_ZA': return ($number == 1) ? 0 : 1; case 'am': case 'am_ET': case 'bh': case 'fil': case 'fil_PH': case 'fr': case 'fr_BE': case 'fr_CA': case 'fr_CH': case 'fr_FR': case 'fr_LU': case 'gun': case 'hi': case 'hi_IN': case 'hy': case 'hy_AM': case 'ln': case 'ln_CD': case 'mg': case 'mg_MG': case 'nso': case 'nso_ZA': case 'ti': case 'ti_ER': case 'ti_ET': case 'wa': case 'wa_BE': case 'xbr': return (($number == 0) || ($number == 1)) ? 0 : 1; case 'be': case 'be_BY': case 'bs': case 'bs_BA': case 'hr': case 'hr_HR': case 'ru': case 'ru_RU': case 'ru_UA': case 'sr': case 'sr_ME': case 'sr_RS': case 'uk': case 'uk_UA': return (($number % 10 == 1) && ($number % 100 != 11)) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2); case 'cs': case 'cs_CZ': case 'sk': case 'sk_SK': return ($number == 1) ? 0 : ((($number >= 2) && ($number <= 4)) ? 1 : 2); case 'ga': case 'ga_IE': return ($number == 1) ? 0 : (($number == 2) ? 1 : 2); case 'lt': case 'lt_LT': return (($number % 10 == 1) && ($number % 100 != 11)) ? 0 : ((($number % 10 >= 2) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2); case 'sl': case 'sl_SI': return ($number % 100 == 1) ? 0 : (($number % 100 == 2) ? 1 : ((($number % 100 == 3) || ($number % 100 == 4)) ? 2 : 3)); case 'mk': case 'mk_MK': return ($number % 10 == 1) ? 0 : 1; case 'mt': case 'mt_MT': return ($number == 1) ? 0 : ((($number == 0) || (($number % 100 > 1) && ($number % 100 < 11))) ? 1 : ((($number % 100 > 10) && ($number % 100 < 20)) ? 2 : 3)); case 'lv': case 'lv_LV': return ($number == 0) ? 0 : ((($number % 10 == 1) && ($number % 100 != 11)) ? 1 : 2); case 'pl': case 'pl_PL': return ($number == 1) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 12) || ($number % 100 > 14))) ? 1 : 2); case 'cy': case 'cy_GB': return ($number == 1) ? 0 : (($number == 2) ? 1 : ((($number == 8) || ($number == 11)) ? 2 : 3)); case 'ro': case 'ro_RO': return ($number == 1) ? 0 : ((($number == 0) || (($number % 100 > 0) && ($number % 100 < 20))) ? 1 : 2); case 'ar': case 'ar_AE': case 'ar_BH': case 'ar_DZ': case 'ar_EG': case 'ar_IN': case 'ar_IQ': case 'ar_JO': case 'ar_KW': case 'ar_LB': case 'ar_LY': case 'ar_MA': case 'ar_OM': case 'ar_QA': case 'ar_SA': case 'ar_SD': case 'ar_SS': case 'ar_SY': case 'ar_TN': case 'ar_YE': return ($number == 0) ? 0 : (($number == 1) ? 1 : (($number == 2) ? 2 : ((($number % 100 >= 3) && ($number % 100 <= 10)) ? 3 : ((($number % 100 >= 11) && ($number % 100 <= 99)) ? 4 : 5)))); default: return 0; } } } ArrayLoader.php000064400000003117150553623110007465 0ustar00messages[$namespace][$locale][$group] ?? []; } /** * Add a new namespace to the loader. * * @param string $namespace * @param string $hint * @return void */ public function addNamespace($namespace, $hint) { // } /** * Add a new JSON path to the loader. * * @param string $path * @return void */ public function addJsonPath($path) { // } /** * Add messages to the loader. * * @param string $locale * @param string $group * @param array $messages * @param string|null $namespace * @return $this */ public function addMessages($locale, $group, array $messages, $namespace = null) { $namespace = $namespace ?: '*'; $this->messages[$namespace][$locale][$group] = $messages; return $this; } /** * Get an array of all the registered namespaces. * * @return array */ public function namespaces() { return []; } }