123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467 |
- <?php
- namespace Grav\Common\Language;
- use Grav\Common\Grav;
- /**
- * Language and translation functionality for Grav
- */
- class Language
- {
- protected $grav;
- protected $enabled = true;
- protected $languages = [];
- protected $page_extensions = [];
- protected $fallback_languages = [];
- protected $default;
- protected $active = null;
- protected $config;
- protected $http_accept_language;
- protected $lang_in_url = false;
- /**
- * Constructor
- *
- * @param \Grav\Common\Grav $grav
- */
- public function __construct(Grav $grav)
- {
- $this->grav = $grav;
- $this->config = $grav['config'];
- $this->languages = $this->config->get('system.languages.supported', []);
- $this->init();
- }
- /**
- * Initialize the default and enabled languages
- */
- public function init()
- {
- $this->default = reset($this->languages);
- if (empty($this->languages)) {
- $this->enabled = false;
- }
- }
- /**
- * Ensure that languages are enabled
- *
- * @return bool
- */
- public function enabled()
- {
- return $this->enabled;
- }
- /**
- * Gets the array of supported languages
- *
- * @return array
- */
- public function getLanguages()
- {
- return $this->languages;
- }
- /**
- * Sets the current supported languages manually
- *
- * @param $langs
- */
- public function setLanguages($langs)
- {
- $this->languages = $langs;
- $this->init();
- }
- /**
- * Gets a pipe-separated string of available languages
- *
- * @return string
- */
- public function getAvailable()
- {
- $languagesArray = $this->languages; //Make local copy
- sort($languagesArray);
- return implode('|', array_reverse($languagesArray));
- }
- /**
- * Gets language, active if set, else default
- *
- * @return mixed
- */
- public function getLanguage()
- {
- return $this->active ? $this->active : $this->default;
- }
- /**
- * Gets current default language
- *
- * @return mixed
- */
- public function getDefault()
- {
- return $this->default;
- }
- /**
- * Sets default language manually
- *
- * @param $lang
- *
- * @return bool
- */
- public function setDefault($lang)
- {
- if ($this->validate($lang)) {
- $this->default = $lang;
- return $lang;
- }
- return false;
- }
- /**
- * Gets current active language
- *
- * @return mixed
- */
- public function getActive()
- {
- return $this->active;
- }
- /**
- * Sets active language manually
- *
- * @param $lang
- *
- * @return bool
- */
- public function setActive($lang)
- {
- if ($this->validate($lang)) {
- $this->active = $lang;
- return $lang;
- }
- return false;
- }
- /**
- * Sets the active language based on the first part of the URL
- *
- * @param $uri
- *
- * @return mixed
- */
- public function setActiveFromUri($uri)
- {
- $regex = '/(^\/(' . $this->getAvailable() . '))(?:\/.*|$)/i';
- // if languages set
- if ($this->enabled()) {
- // try setting from prefix of URL (/en/blah/blah)
- if (preg_match($regex, $uri, $matches)) {
- $this->lang_in_url = true;
- $this->active = $matches[2];
- $uri = preg_replace("/\\" . $matches[1] . "/", '', $matches[0], 1);
- // store in session if different
- if ($this->config->get('system.session.enabled', false)
- && $this->config->get('system.languages.session_store_active', true)
- && $this->grav['session']->active_language != $this->active
- ) {
- $this->grav['session']->active_language = $this->active;
- }
- } else {
- // try getting from session, else no active
- if ($this->config->get('system.session.enabled', false) &&
- $this->config->get('system.languages.session_store_active', true)) {
- $this->active = $this->grav['session']->active_language ?: null;
- }
- // if still null, try from http_accept_language header
- if ($this->active === null && $this->config->get('system.languages.http_accept_language')) {
- $preferred = $this->getBrowserLanguages();
- foreach ($preferred as $lang) {
- if ($this->validate($lang)) {
- $this->active = $lang;
- break;
- }
- }
- }
- }
- }
- return $uri;
- }
- /**
- * Get's a URL prefix based on configuration
- *
- * @param null $lang
- * @return string
- */
- public function getLanguageURLPrefix($lang = null)
- {
- // if active lang is not passed in, use current active
- if (!$lang) {
- $lang = $this->getLanguage();
- }
- return $this->isIncludeDefaultLanguage($lang) ? '/' . $lang : '';
- }
- /**
- * Test to see if language is default and language should be included in the URL
- *
- * @param null $lang
- * @return bool
- */
- public function isIncludeDefaultLanguage($lang = null)
- {
- // if active lang is not passed in, use current active
- if (!$lang) {
- $lang = $this->getLanguage();
- }
- if ($this->default == $lang && $this->config->get('system.languages.include_default_lang') === false) {
- return false;
- } else {
- return true;
- }
- }
- /**
- * Simple getter to tell if a language was found in the URL
- *
- * @return bool
- */
- public function isLanguageInUrl()
- {
- return (bool) $this->lang_in_url;
- }
- /**
- * Gets an array of valid extensions with active first, then fallback extensions
- *
- * @return array
- */
- public function getFallbackPageExtensions($file_ext = null)
- {
- if (empty($this->page_extensions)) {
- if (empty($file_ext)) {
- $file_ext = CONTENT_EXT;
- }
- if ($this->enabled()) {
- $valid_lang_extensions = [];
- foreach ($this->languages as $lang) {
- $valid_lang_extensions[] = '.' . $lang . $file_ext;
- }
- if ($this->active) {
- $active_extension = '.' . $this->active . $file_ext;
- $key = array_search($active_extension, $valid_lang_extensions);
- unset($valid_lang_extensions[$key]);
- array_unshift($valid_lang_extensions, $active_extension);
- }
- $this->page_extensions = array_merge($valid_lang_extensions, (array)$file_ext);
- } else {
- $this->page_extensions = (array)$file_ext;
- }
- }
- return $this->page_extensions;
- }
- /**
- * Gets an array of languages with active first, then fallback languages
- *
- * @return array
- */
- public function getFallbackLanguages()
- {
- if (empty($this->fallback_languages)) {
- if ($this->enabled()) {
- $fallback_languages = $this->languages;
- if ($this->active) {
- $active_extension = $this->active;
- $key = array_search($active_extension, $fallback_languages);
- unset($fallback_languages[$key]);
- array_unshift($fallback_languages, $active_extension);
- }
- $this->fallback_languages = $fallback_languages;
- }
- // always add english in case a translation doesn't exist
- $this->fallback_languages[] = 'en';
- }
- return $this->fallback_languages;
- }
- /**
- * Ensures the language is valid and supported
- *
- * @param $lang
- *
- * @return bool
- */
- public function validate($lang)
- {
- if (in_array($lang, $this->languages)) {
- return true;
- }
- return false;
- }
- /**
- * Translate a key and possibly arguments into a string using current lang and fallbacks
- *
- * @param $args first argument is the lookup key value
- * other arguments can be passed and replaced in the translation with sprintf syntax
- * @param Array $languages
- * @param bool $array_support
- * @param bool $html_out
- *
- * @return string
- */
- public function translate($args, Array $languages = null, $array_support = false, $html_out = false)
- {
- if (is_array($args)) {
- $lookup = array_shift($args);
- } else {
- $lookup = $args;
- $args = [];
- }
- if ($this->config->get('system.languages.translations', true)) {
- if ($this->enabled() && $lookup) {
- if (empty($languages)) {
- if ($this->config->get('system.languages.translations_fallback', true)) {
- $languages = $this->getFallbackLanguages();
- } else {
- $languages = (array)$this->getDefault();
- }
- }
- } else {
- $languages = ['en'];
- }
- foreach ((array)$languages as $lang) {
- $translation = $this->getTranslation($lang, $lookup, $array_support);
- if ($translation) {
- if (count($args) >= 1) {
- return vsprintf($translation, $args);
- } else {
- return $translation;
- }
- }
- }
- }
- if ($html_out) {
- return '<span class="untranslated">' . $lookup . '</span>';
- } else {
- return $lookup;
- }
- }
- /**
- * Translate Array
- *
- * @param $key
- * @param $index
- * @param null $languages
- * @param bool $html_out
- *
- * @return string
- */
- public function translateArray($key, $index, $languages = null, $html_out = false)
- {
- if ($this->config->get('system.languages.translations', true)) {
- if ($this->enabled() && $key) {
- if (empty($languages)) {
- if ($this->config->get('system.languages.translations_fallback', true)) {
- $languages = $this->getFallbackLanguages();
- } else {
- $languages = (array)$this->getDefault();
- }
- }
- } else {
- $languages = ['en'];
- }
- foreach ((array)$languages as $lang) {
- $translation_array = (array)$this->config->getLanguages()->get($lang . '.' . $key, null);
- if ($translation_array && array_key_exists($index, $translation_array)) {
- return $translation_array[$index];
- }
- }
- }
- if ($html_out) {
- return '<span class="untranslated">' . $key . '[' . $index . ']</span>';
- } else {
- return $key . '[' . $index . ']';
- }
- }
- /**
- * Lookup the translation text for a given lang and key
- *
- * @param $lang lang code
- * @param $key key to lookup with
- * @param bool $array_support
- *
- * @return string
- */
- public function getTranslation($lang, $key, $array_support = false)
- {
- $translation = $this->config->getLanguages()->get($lang . '.' . $key, null);
- if (!$array_support && is_array($translation)) {
- return (string)array_shift($translation);
- }
- return $translation;
- }
- public function getBrowserLanguages($accept_langs = [])
- {
- if (empty($this->http_accept_language)) {
- if (empty($accept_langs) && isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
- $accept_langs = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
- } else {
- return $accept_langs;
- }
- foreach (explode(',', $accept_langs) as $k => $pref) {
- // split $pref again by ';q='
- // and decorate the language entries by inverted position
- if (false !== ($i = strpos($pref, ';q='))) {
- $langs[substr($pref, 0, $i)] = array((float)substr($pref, $i + 3), -$k);
- } else {
- $langs[$pref] = array(1, -$k);
- }
- }
- arsort($langs);
- // no need to undecorate, because we're only interested in the keys
- $this->http_accept_language = array_keys($langs);
- }
- return $this->http_accept_language;
- }
- }
|