From 941580bacf7646e7e45f1af19c36e6169f308f2f Mon Sep 17 00:00:00 2001 From: RealStickman Date: Sat, 19 Nov 2022 19:41:01 +0100 Subject: [PATCH] (Grav GitSync) Automatic Commit from RealStickman --- plugins/langswitcher/CHANGELOG.md | 118 +++++ plugins/langswitcher/LICENSE | 21 + plugins/langswitcher/README.md | 128 +++++ plugins/langswitcher/assets/readme_1.png | Bin 0 -> 38637 bytes plugins/langswitcher/blueprints.yaml | 73 +++ plugins/langswitcher/composer.json | 29 ++ plugins/langswitcher/composer.lock | 24 + plugins/langswitcher/css/langswitcher.css | 22 + plugins/langswitcher/langswitcher.php | 180 +++++++ plugins/langswitcher/langswitcher.yaml | 5 + plugins/langswitcher/languages.yaml | 35 ++ .../partials/langswitcher-long.html.twig | 1 + .../partials/langswitcher-short.html.twig | 1 + .../partials/langswitcher.hreflang.html.twig | 16 + .../templates/partials/langswitcher.html.twig | 33 ++ plugins/langswitcher/vendor/autoload.php | 7 + .../vendor/composer/ClassLoader.php | 445 ++++++++++++++++++ plugins/langswitcher/vendor/composer/LICENSE | 21 + .../vendor/composer/autoload_classmap.php | 10 + .../vendor/composer/autoload_namespaces.php | 9 + .../vendor/composer/autoload_psr4.php | 9 + .../vendor/composer/autoload_real.php | 52 ++ .../vendor/composer/autoload_static.php | 20 + .../vendor/composer/installed.json | 1 + 24 files changed, 1260 insertions(+) create mode 100644 plugins/langswitcher/CHANGELOG.md create mode 100644 plugins/langswitcher/LICENSE create mode 100644 plugins/langswitcher/README.md create mode 100644 plugins/langswitcher/assets/readme_1.png create mode 100644 plugins/langswitcher/blueprints.yaml create mode 100644 plugins/langswitcher/composer.json create mode 100644 plugins/langswitcher/composer.lock create mode 100644 plugins/langswitcher/css/langswitcher.css create mode 100644 plugins/langswitcher/langswitcher.php create mode 100644 plugins/langswitcher/langswitcher.yaml create mode 100644 plugins/langswitcher/languages.yaml create mode 100644 plugins/langswitcher/templates/partials/langswitcher-long.html.twig create mode 100644 plugins/langswitcher/templates/partials/langswitcher-short.html.twig create mode 100644 plugins/langswitcher/templates/partials/langswitcher.hreflang.html.twig create mode 100644 plugins/langswitcher/templates/partials/langswitcher.html.twig create mode 100644 plugins/langswitcher/vendor/autoload.php create mode 100644 plugins/langswitcher/vendor/composer/ClassLoader.php create mode 100644 plugins/langswitcher/vendor/composer/LICENSE create mode 100644 plugins/langswitcher/vendor/composer/autoload_classmap.php create mode 100644 plugins/langswitcher/vendor/composer/autoload_namespaces.php create mode 100644 plugins/langswitcher/vendor/composer/autoload_psr4.php create mode 100644 plugins/langswitcher/vendor/composer/autoload_real.php create mode 100644 plugins/langswitcher/vendor/composer/autoload_static.php create mode 100644 plugins/langswitcher/vendor/composer/installed.json diff --git a/plugins/langswitcher/CHANGELOG.md b/plugins/langswitcher/CHANGELOG.md new file mode 100644 index 0000000..165780c --- /dev/null +++ b/plugins/langswitcher/CHANGELOG.md @@ -0,0 +1,118 @@ +# v3.0.2 +## 10/05/2022 + +1. [](#new) + * Require Grav `1.7.37` to make use of the new `Pages::getSimplePagesHash()` method + * Added caching to `translated_routes` so translation work is only performed on the first load, resulting in faster subsequent page loads + +# v3.0.1 +## 08/19/2022 + +1. [](#bugfix) + * Fixed another issue with incorrect `hreflang` URLs + +# v3.0.0 +## 08/19/2022 + +1. [](#new) + * Completely rewrote the logic for translated URLs to be more robust. + * Added configuration option to use **Translated URLs** or use previous **Raw-Route** approach +1. [](#improved) + * Updated `hreflang` Twig template to use new translated URLs logic + * Added an `x-default` entry for `hreflang` template when default language has `include_default_lang` set to false + * Support `params` and `query` string parameters in URLs + * Full domain URLs for `hreflang` entries + +# v2.0.1 +## 08/04/2022 + +1. [](#bugfix) + * Fixed exception thrown instead of **404 Page not found** [#66](https://github.com/getgrav/grav-plugin-langswitcher/issues/66) + +# v2.0.0 +## 07/25/2022 + +1. [](#new) + * Support for translated slugs!!!! [#50](https://github.com/getgrav/grav-plugin-langswitcher/pull/50) + * Require Grav `1.7` +1. [](#improved) + * Improved support for home URL [#59](https://github.com/getgrav/grav-plugin-langswitcher/pull/59) + +# v1.5.0 +## 07/01/2021 + +1. [](#new) + * Made langswitcher display more customizable. See README.md for full details. + +# v1.4.3 +## 06/25/2021 + +1. [](#new) + * Made langswitcher data available in Grav object +1. [](#bugfix) + * Fix multilang alternatives [#58](https://github.com/getgrav/grav-plugin-langswitcher/pull/58) +# v1.4.2 +## 03/17/2021 + +1. [](#new) + * Pass phpstan level 1 tests + * Require Grav v1.6 +1. [](#bugfix) + * Fix `hreflang` URLs [#57](https://github.com/getgrav/grav-plugin-langswitcher/pull/57) + +# v1.4.1 +## 05/09/2019 + +1. [](#new) + * Added some translations [#45](https://github.com/getgrav/grav-plugin-langswitcher/pull/45) + +# v1.4.0 +## 06/29/2017 + +1. [](#new) + * Added the `untranslated_pages_behavior` option to determine what to do with a language link when the current page doesn't exist in that language or it exists but it's not published +1. [](#bugfix) + * Fixed generated URLs when `append_url_extension` is set, via PR [#22](https://github.com/getgrav/grav-plugin-langswitcher/pull/22) + +# v1.3.0 +## 02/17/2017 + +1. [](#new) + * Added support for `hreflang` annotations via PR [#19](https://github.com/getgrav/grav-plugin-langswitcher/pull/19) + +# v1.2.1 +## 05/28/2016 + +1. [](#bugfix) + * Display all language names, even those with non supported locales + +# v1.2.0 +## 05/03/2016 + +1. [](#improved) + * Take URI parameters into account when switching languages + * Add `external` class to avoid problems on modular pages when `jquery.singlePageNav` is loaded + +# v1.1.0 +## 10/15/2015 + +1. [](#improved) + * Added active class to language links + +# v1.0.2 +## 07/13/2015 + +1. [](#improved) + * Improved homepage routing + +# v1.0.1 +## 07/08/2015 + +1. [](#improved) + * Updated blueprints with some typo fixes + +# v1.0.0 +## 07/08/2015 + +1. [](#new) + * ChangeLog started... diff --git a/plugins/langswitcher/LICENSE b/plugins/langswitcher/LICENSE new file mode 100644 index 0000000..0e788c6 --- /dev/null +++ b/plugins/langswitcher/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Grav + +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. diff --git a/plugins/langswitcher/README.md b/plugins/langswitcher/README.md new file mode 100644 index 0000000..81bcc04 --- /dev/null +++ b/plugins/langswitcher/README.md @@ -0,0 +1,128 @@ +# Grav LangSwitcher Plugin + +![LangSwitcher](assets/readme_1.png) + +`LangSwitcher` is a [Grav](http://github.com/getgrav/grav) plugin that provides native language text links to switch between [Multiple Languages](http://learn.getgrav.org/content/multi-language) in Grav **0.9.30** or greater. + +# Installation + +Installing the LangSwitcher plugin can be done in one of two ways. Our GPM (Grav Package Manager) installation method enables you to quickly and easily install the plugin with a simple terminal command, while the manual method enables you to do so via a zip file. + +## GPM Installation (Preferred) + +The simplest way to install this plugin is via the [Grav Package Manager (GPM)](http://learn.getgrav.org/advanced/grav-gpm) through your system's Terminal (also called the command line). From the root of your Grav install type: + + bin/gpm install langswitcher + +This will install the LangSwitcher plugin into your `/user/plugins` directory within Grav. Its files can be found under `/your/site/grav/user/plugins/langswitcher`. + +## Manual Installation + +To install this plugin, just download the zip version of this repository and unzip it under `/your/site/grav/user/plugins`. Then, rename the folder to `langswitcher`. You can find these files either on [GitHub](https://github.com/getgrav/grav-plugin-langswitcher) or via [GetGrav.org](http://getgrav.org/downloads/plugins#extras). + +You should now have all the plugin files under + + /your/site/grav/user/plugins/langswitcher + +# Usage + +The `langswitcher` plugin doesn't require any configuration. You do however need to add the included Twig partials template into your own theme somewhere you want the available languages to be displayed. + +```twig +{% include 'partials/langswitcher.html.twig' %} +``` + +Something you might want to do is to override the look and feel of the langswitcher, and with Grav it is super easy. + +Copy the template file [langswitcher.html.twig](templates/partials/langswitcher.html.twig) into the `templates` folder of your custom theme: + +``` +/your/site/grav/user/themes/custom-theme/templates/partials/langswitcher.html.twig +``` + +You can now edit the override and tweak it however you prefer. + +## Usage of the `hreflang` partial + +A second template is available for `hreflang` annotations in the header of the page. In order to emit language annotations for the available languages of a page you need to add the corrsponding Twig partial template into the `` section of your page, which can typically be found in `base.html.twig`: + +```twkg +{% include 'partials/langswitcher.hreflang.html.twig' %} +``` + +This will generate something like: + +```html + + + +``` + +# Updating + +As development for the LangSwitcher plugin continues, new versions may become available that add additional features and functionality, improve compatibility with newer Grav releases, and generally provide a better user experience. Updating LangSwitcher is easy, and can be done through Grav's GPM system, as well as manually. + +## GPM Update (Preferred) + +The simplest way to update this plugin is via the [Grav Package Manager (GPM)](http://learn.getgrav.org/advanced/grav-gpm). You can do this with this by navigating to the root directory of your Grav install using your system's Terminal (also called command line) and typing the following: + + bin/gpm update langswitcher + +This command will check your Grav install to see if your LangSwitcher plugin is due for an update. If a newer release is found, you will be asked whether or not you wish to update. To continue, type `y` and hit enter. The plugin will automatically update and clear Grav's cache. + +> Note: Any changes you have made to any of the files listed under this directory will also be removed and replaced by the new set. Any files located elsewhere (for example a YAML settings file placed in `user/config/plugins`) will remain intact. + +## Configuration + +Simply copy the `user/plugins/langswitcher/langswitcher.yaml` into `user/config/plugins/langswitcher.yaml` and make your modifications. + +```yaml +enabled: true +built_in_css: true +translated_urls: true +untranslated_pages_behavior: none +language_display: long +``` + +Options are pretty self explanatory. + +## Redirecting after switching language + +To have Grav redirect to the default page route after switching language, you must add the following configuration to `user/config/system.yaml` + +```yaml +pages: + redirect_default_route: true +``` + +## Customization + +The default format for the displaying of the languages is to use the native language names in a **long** format (e.g. `English`, `Deutsch`, `Français`). However, you can change the default output to use **short** names (e.g. `EN`, `DE`, `FR`). + +This can be configured via the `langswitcher.yaml` configuration file: + +```yaml +language_display: long # long | short are the valid options +``` + +You can also pass the format in directly via the Twig include: + +```twig +{% include 'partials/langswitcher.hreflang.html.twig' with {display_format: 'short'} %} +``` + +Also you can override the two Twig partials that control the actual display of the **long** and **short** output, by copying the partial int your theme's `templates/partials/` folder and modifying: + +```twig +# templates/partials/langswitcher-long.html.twig +{{ native_name(language)|capitalize }} +``` + +and + +```twig +# templates/partials/langswitcher-short.html.twig +{{ language|upper }} +``` + + diff --git a/plugins/langswitcher/assets/readme_1.png b/plugins/langswitcher/assets/readme_1.png new file mode 100644 index 0000000000000000000000000000000000000000..8cfb824862c0dac71366cbed80d8f33046b9c9f8 GIT binary patch literal 38637 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

`*d8!x`?= z7aRPE>2+vT>$*#VpcfrA6_G=B4Ih3g*h_}s5My)QF2i+6hA)WWaSxdV!<*y0>`{l)Z+;`+b;$P~IQn~Y`DKWHm{_vclYI)~6mz=! z@L-tRBfc8+ARHQo#I~vw-tshR>+&YaqbpKvV@%c~Yc#hqjLxN2=j(2XBk73Shb3>; zQ&OeqFmv#)EwB6z#k*ODv=85W?sIQG^PO*g<7?sY%=r3yd4zz&%yJD6+~eRNYa+4K~Hm%F-~EHUvw$sBbrh1 z6JC*+@-qz!i8pT|JS4#BSz*=@$!G#6eCJXFkZZ#$ZBxiZ!OMmrhs#Lsv4t-Vg1xaD zvP=p0@U^{B>dMn-1wmF^@AkLmf=|Y#SX_iy4e_krhW;7xH%n9Q`_gz)>)J{6cO@1Vzqa~{LWM!!0Au{qe-tv^Q3 zZ_?CAZMP9e>JNVEDYtbNH%oXWBo%l^!#skzpp7x^(MN|YCQ%bw^0v8FGw4gme)fVx zY;XQ7&ZpkZd_Vi0Z@%`MZ@%>9H}8J(&G&xqo6r2NH^1?J-+b!Rd7$$DUVrf0{|~); Vd!ukp-LL=v002ovPDHLkV1k|N4YB|L literal 0 HcmV?d00001 diff --git a/plugins/langswitcher/blueprints.yaml b/plugins/langswitcher/blueprints.yaml new file mode 100644 index 0000000..910baee --- /dev/null +++ b/plugins/langswitcher/blueprints.yaml @@ -0,0 +1,73 @@ +name: LangSwitcher +version: 3.0.2 +description: LangSwitcher is a [Grav](https://github.com/getgrav/grav) plugin that provides native language text links to switch between [multiple languages](http://learn.getgrav.org/content/multi-language) in Grav **v0.9.30** or greater. +icon: globe +author: + name: Team Grav + email: devs@getgrav.org + url: http://getgrav.org +homepage: https://github.com/getgrav/grav-plugin-langswitcher +keywords: mulitlang, multilanguage, translation, switcher +bugs: https://github.com/getgrav/grav-plugin-langswitcher/issues +license: MIT +dependencies: + - { name: grav, version: '>=1.7.37' } + +form: + validation: strict + fields: + enabled: + type: toggle + label: PLUGIN_ADMIN.PLUGIN_STATUS + highlight: 1 + default: 1 + options: + 1: PLUGIN_ADMIN.ENABLED + 0: PLUGIN_ADMIN.DISABLED + validate: + type: bool + + built_in_css: + type: toggle + label: PLUGIN_LANGSWITCHER.BUILTIN_CSS + help: PLUGIN_LANGSWITCHER.BUILTIN_CSS_HELP + highlight: 1 + default: 1 + options: + 1: PLUGIN_ADMIN.ENABLED + 0: PLUGIN_ADMIN.DISABLED + validate: + type: bool + + translated_urls: + type: toggle + label: PLUGIN_LANGSWITCHER.TRANSLATED_URLS + help: PLUGIN_LANGSWITCHER.TRANSLATED_URLS_HELP + highlight: 1 + default: 1 + options: + 1: PLUGIN_ADMIN.ENABLED + 0: PLUGIN_ADMIN.DISABLED + validate: + type: bool + + language_display: + type: select + size: small + label: PLUGIN_LANGSWITCHER.LANGUAGE_DISPLAY + help: PLUGIN_LANGSWITCHER.LANGUAGE_DISPLAY_HELP + default: long + options: + long: PLUGIN_LANGSWITCHER.LANGUAGE_DISPLAY_LONG + short: PLUGIN_LANGSWITCHER.LANGUAGE_DISPLAY_SHORT + + untranslated_pages_behavior: + type: select + size: medium + label: PLUGIN_LANGSWITCHER.UNTRANSLATED_PAGES_BEHAVIOR + help: PLUGIN_LANGSWITCHER.UNTRANSLATED_PAGES_BEHAVIOR_HELP + default: none + options: + none: PLUGIN_LANGSWITCHER.UNTRANSLATED_PAGES_BEHAVIOR_OPTION_NONE + redirect: PLUGIN_LANGSWITCHER.UNTRANSLATED_PAGES_BEHAVIOR_OPTION_REDIRECT + hide: PLUGIN_LANGSWITCHER.UNTRANSLATED_PAGES_BEHAVIOR_OPTION_HIDE diff --git a/plugins/langswitcher/composer.json b/plugins/langswitcher/composer.json new file mode 100644 index 0000000..d32049d --- /dev/null +++ b/plugins/langswitcher/composer.json @@ -0,0 +1,29 @@ +{ + "name": "grav-plugin-langswitcher", + "type": "grav-plugin", + "description": "Language switcher plugin for Grav CMS", + "keywords": ["langswitcher"], + "homepage": "https://github.com/getgrav/grav-plugin-langswitcher/", + "license": "MIT", + "authors": [ + { + "name": "Team Grav", + "email": "devs@getgrav.org", + "homepage": "http://getgrav.org", + "role": "Developer" + } + ], + "require": { + "php": ">=7.1.3", + "ext-json": "*", + "ext-mbstring": "*" + }, + "autoload": { + "classmap": ["langswitcher.php"] + }, + "config": { + "platform": { + "php": "7.1.3" + } + } +} diff --git a/plugins/langswitcher/composer.lock b/plugins/langswitcher/composer.lock new file mode 100644 index 0000000..232467f --- /dev/null +++ b/plugins/langswitcher/composer.lock @@ -0,0 +1,24 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "54479277654e1741212fe76198612572", + "packages": [], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=7.1.3", + "ext-json": "*", + "ext-mbstring": "*" + }, + "platform-dev": [], + "platform-overrides": { + "php": "7.1.3" + } +} diff --git a/plugins/langswitcher/css/langswitcher.css b/plugins/langswitcher/css/langswitcher.css new file mode 100644 index 0000000..710dee3 --- /dev/null +++ b/plugins/langswitcher/css/langswitcher.css @@ -0,0 +1,22 @@ +.langswitcher { + position: relative; + top: 50%; + -webkit-transform: translateY(-50%); + -moz-transform: translateY(-50%); + -o-transform: translateY(-50%); + -ms-transform: translateY(-50%); + transform: translateY(-50%); + margin-left: 1rem !important; + display: inline-block; +} + +.langswitcher li { + display: inline-block; + margin-left: 0.5rem; + line-height: 1rem; +} + +.langswitcher .active { + font-weight: bold; + text-decoration: underline; +} diff --git a/plugins/langswitcher/langswitcher.php b/plugins/langswitcher/langswitcher.php new file mode 100644 index 0000000..82f22eb --- /dev/null +++ b/plugins/langswitcher/langswitcher.php @@ -0,0 +1,180 @@ + [ + ['autoload', 100001], + ['onPluginsInitialized', 0] + ] + ]; + } + + /** + * [onPluginsInitialized:100000] Composer autoload. + * + * @return ClassLoader + */ + public function autoload() + { + return require __DIR__ . '/vendor/autoload.php'; + } + + /** + * Initialize configuration + */ + public function onPluginsInitialized() + { + if ($this->isAdmin()) { + $this->active = false; + return; + } + + $this->enable([ + 'onTwigInitialized' => ['onTwigInitialized', 0], + 'onTwigTemplatePaths' => ['onTwigTemplatePaths', 0], + 'onTwigSiteVariables' => ['onTwigSiteVariables', 0] + ]); + } + + /** Add the native_name function */ + public function onTwigInitialized() + { + $this->grav['twig']->twig()->addFunction( + new \Twig_SimpleFunction('native_name', function($key) { + return LanguageCodes::getNativeName($key); + }) + ); + } + + /** + * Add current directory to twig lookup paths. + */ + public function onTwigTemplatePaths() + { + $this->grav['twig']->twig_paths[] = __DIR__ . '/templates'; + } + + /** + * Generate localized route based on the translated slugs found through the pages hierarchy + */ + protected function getTranslatedUrl($lang, $path) + { + /** @var Language $language */ + $url = null; + /** @var Pages $pages */ + $pages = $this->grav['pages']; + /** @var Language $language */ + $language = $this->grav['language']; + + $language->init(); + $language->setActive($lang); + $pages->reset(); + $page = $pages->get($path); + if ($page) { + $url = $page->url(); + } + return $url; + } + + /** + * Set needed variables to display Langswitcher. + */ + public function onTwigSiteVariables() + { + + /** @var PageInterface $page */ + $page = $this->grav['page']; + + /** @var Pages $pages */ + $pages = $this->grav['pages']; + + /** @var Cache $cache */ + $cache = $this->grav['cache']; + + $data = new \stdClass; + $data->page_route = $page->rawRoute(); + if ($page->home()) { + $data->page_route = '/'; + } + + $translated_cache_key = md5('translated_cache_key'.$data->page_route.$pages->getSimplePagesHash()); + + $languages = $this->grav['language']->getLanguages(); + $data->languages = $languages; + + if ($this->config->get('plugins.langswitcher.untranslated_pages_behavior') !== 'none') { + $translated_pages = []; + foreach ($languages as $language) { + $translated_pages[$language] = null; + $page_name_without_ext = substr($page->name(), 0, -(strlen($page->extension()))); + $translated_page_path = $page->path() . DS . $page_name_without_ext . '.' . $language . '.md'; + if (!file_exists($translated_page_path) and $language == $this->grav['language']->getDefault()) { + $translated_page_path = $page->path() . DS . $page_name_without_ext . '.md'; + } + if (file_exists($translated_page_path)) { + $translated_page = new Page(); + $translated_page->init(new \SplFileInfo($translated_page_path), $language . '.md'); + $translated_pages[$language] = $translated_page; + } + } + $data->translated_pages = $translated_pages; + } + + $language = $this->grav['language']; + $active = $language->getActive() ?? $language->getDefault(); + + if ($this->config->get('plugins.langswitcher.translated_urls', true)) { + $data->translated_routes = $cache->fetch($translated_cache_key); + + if ($data->translated_routes === false) { + $translate_langs = $data->languages; + + if (($key = array_search($active, $translate_langs)) !== false) { + $data->translated_routes[$active] = $page->url(); + unset($translate_langs[$key]); + } + + foreach ($translate_langs as $lang) { + $data->translated_routes[$lang] = $this->getTranslatedUrl($lang, $page->path()); + if (is_null($data->translated_routes[$lang])) { + $data->translated_routes[$lang] = $data->page_route; + } + } + // Reset pages to current active language + $language->init(); + $language->setActive($active); + $this->grav['pages']->reset(); + $cache->save($translated_cache_key, $data->translated_routes); + } + } + + $data->current = $language->getLanguage(); + + $this->grav['twig']->twig_vars['langswitcher'] = $this->grav['langswitcher'] = $data; + + if ($this->config->get('plugins.langswitcher.built_in_css')) { + $this->grav['assets']->add('plugin://langswitcher/css/langswitcher.css'); + } + } + + public function getNativeName($code) { + + } +} diff --git a/plugins/langswitcher/langswitcher.yaml b/plugins/langswitcher/langswitcher.yaml new file mode 100644 index 0000000..a4973cd --- /dev/null +++ b/plugins/langswitcher/langswitcher.yaml @@ -0,0 +1,5 @@ +enabled: true +built_in_css: true +translated_urls: true +untranslated_pages_behavior: none +language_display: long diff --git a/plugins/langswitcher/languages.yaml b/plugins/langswitcher/languages.yaml new file mode 100644 index 0000000..b2ece9f --- /dev/null +++ b/plugins/langswitcher/languages.yaml @@ -0,0 +1,35 @@ +en: + PLUGIN_LANGSWITCHER: + BUILTIN_CSS: 'Use built in CSS' + BUILTIN_CSS_HELP: 'Include the CSS provided by the LangSwitcher plugin.' + UNTRANSLATED_PAGES_BEHAVIOR: 'Untranslated pages behavior' + UNTRANSLATED_PAGES_BEHAVIOR_HELP: "Determine what to do with a language link when the current page doesn't exist in that language or it exists but it's not published." + UNTRANSLATED_PAGES_BEHAVIOR_OPTION_NONE: 'Show language (default)' + UNTRANSLATED_PAGES_BEHAVIOR_OPTION_REDIRECT: 'Show language, link to home route' + UNTRANSLATED_PAGES_BEHAVIOR_OPTION_HIDE: 'Hide language' + LANGUAGE_DISPLAY: 'Language Display' + LANGUAGE_DISPLAY_HELP: 'The format of the language display, either "long" (e.g. English), or "short" (e.g. EN)' + LANGUAGE_DISPLAY_LONG: 'Long' + LANGUAGE_DISPLAY_SHORT: 'Short' + TRANSLATED_URLS: 'Translated URLs' + TRANSLATED_URLS_HELP: 'Use the actual translated page URL rather then the raw-route' + +ru: + PLUGIN_LANGSWITCHER: + BUILTIN_CSS: 'Использовать встроенный CSS' + BUILTIN_CSS_HELP: 'Использовать CSS, предоставленный плагином LangSwitcher.' + UNTRANSLATED_PAGES_BEHAVIOR: 'Поведение непереведенных страниц' + UNTRANSLATED_PAGES_BEHAVIOR_HELP: 'Определяет что делать с языковой ссылкой, если текущая страница не существует на этом языке или существует, но не опубликована.' + UNTRANSLATED_PAGES_BEHAVIOR_OPTION_NONE: 'Показать язык (по умолчанию)' + UNTRANSLATED_PAGES_BEHAVIOR_OPTION_REDIRECT: 'Показать язык, ссылка на домашнюю страницу' + UNTRANSLATED_PAGES_BEHAVIOR_OPTION_HIDE: 'Скрыть язык' + +uk: + PLUGIN_LANGSWITCHER: + BUILTIN_CSS: 'Використовувати вбудований CSS' + BUILTIN_CSS_HELP: 'Використовувати CSS, наданий плагіном LangSwitcher.' + UNTRANSLATED_PAGES_BEHAVIOR: 'Поведінка неперекладених сторінок' + UNTRANSLATED_PAGES_BEHAVIOR_HELP: 'Визначає що робити з посиланням на мову, якщо поточна сторінка не існує на цій мові або існує, але не опублікована.' + UNTRANSLATED_PAGES_BEHAVIOR_OPTION_NONE: 'Показати мову (за умовчанням)' + UNTRANSLATED_PAGES_BEHAVIOR_OPTION_REDIRECT: 'Показати мову, посилання на домашню сторінку' + UNTRANSLATED_PAGES_BEHAVIOR_OPTION_HIDE: 'Приховати мову' diff --git a/plugins/langswitcher/templates/partials/langswitcher-long.html.twig b/plugins/langswitcher/templates/partials/langswitcher-long.html.twig new file mode 100644 index 0000000..0fea2fc --- /dev/null +++ b/plugins/langswitcher/templates/partials/langswitcher-long.html.twig @@ -0,0 +1 @@ +{{ native_name(language)|capitalize }} \ No newline at end of file diff --git a/plugins/langswitcher/templates/partials/langswitcher-short.html.twig b/plugins/langswitcher/templates/partials/langswitcher-short.html.twig new file mode 100644 index 0000000..e71027e --- /dev/null +++ b/plugins/langswitcher/templates/partials/langswitcher-short.html.twig @@ -0,0 +1 @@ +{{ language|upper }} \ No newline at end of file diff --git a/plugins/langswitcher/templates/partials/langswitcher.hreflang.html.twig b/plugins/langswitcher/templates/partials/langswitcher.hreflang.html.twig new file mode 100644 index 0000000..1646ea4 --- /dev/null +++ b/plugins/langswitcher/templates/partials/langswitcher.hreflang.html.twig @@ -0,0 +1,16 @@ +{% set language_obj = grav.language %} +{% for language in langswitcher.languages %} + {% if langswitcher.translated_routes[language] %} + {% set lang_url = langswitcher.translated_routes[language] ~ page.urlExtension %} + {% else %} + {% set base_lang_url = base_url_simple ~ grav.language.getLanguageURLPrefix(language) %} + {% set lang_url = base_lang_url ~ langswitcher.page_route ~ page.urlExtension %} + {% endif %} + + {% set href_url = uri.base ~ lang_url ~ uri.params ~ (uri.query|length > 1 ? '?' ~ uri.query) %} + + {% if (language_obj.default == language and config.languages.include_default_lang == false) %} + + {% endif %} + +{% endfor %} diff --git a/plugins/langswitcher/templates/partials/langswitcher.html.twig b/plugins/langswitcher/templates/partials/langswitcher.html.twig new file mode 100644 index 0000000..3b44b1f --- /dev/null +++ b/plugins/langswitcher/templates/partials/langswitcher.html.twig @@ -0,0 +1,33 @@ +

diff --git a/plugins/langswitcher/vendor/autoload.php b/plugins/langswitcher/vendor/autoload.php new file mode 100644 index 0000000..9211701 --- /dev/null +++ b/plugins/langswitcher/vendor/autoload.php @@ -0,0 +1,7 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Autoload; + +/** + * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. + * + * $loader = new \Composer\Autoload\ClassLoader(); + * + * // register classes with namespaces + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (eg. for PEAR packages) + * $loader->setUseIncludePath(true); + * + * In this example, if you try to use a class in the Symfony\Component + * namespace or one of its children (Symfony\Component\Console for instance), + * the autoloader will first look for the class under the component/ + * directory, and it will then fallback to the framework/ directory if not + * found before giving up. + * + * This class is loosely based on the Symfony UniversalClassLoader. + * + * @author Fabien Potencier + * @author Jordi Boggiano + * @see http://www.php-fig.org/psr/psr-0/ + * @see http://www.php-fig.org/psr/psr-4/ + */ +class ClassLoader +{ + // PSR-4 + private $prefixLengthsPsr4 = array(); + private $prefixDirsPsr4 = array(); + private $fallbackDirsPsr4 = array(); + + // PSR-0 + private $prefixesPsr0 = array(); + private $fallbackDirsPsr0 = array(); + + private $useIncludePath = false; + private $classMap = array(); + private $classMapAuthoritative = false; + private $missingClasses = array(); + private $apcuPrefix; + + public function getPrefixes() + { + if (!empty($this->prefixesPsr0)) { + return call_user_func_array('array_merge', $this->prefixesPsr0); + } + + return array(); + } + + public function getPrefixesPsr4() + { + return $this->prefixDirsPsr4; + } + + public function getFallbackDirs() + { + return $this->fallbackDirsPsr0; + } + + public function getFallbackDirsPsr4() + { + return $this->fallbackDirsPsr4; + } + + public function getClassMap() + { + return $this->classMap; + } + + /** + * @param array $classMap Class to filename map + */ + public function addClassMap(array $classMap) + { + if ($this->classMap) { + $this->classMap = array_merge($this->classMap, $classMap); + } else { + $this->classMap = $classMap; + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, either + * appending or prepending to the ones previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories + */ + public function add($prefix, $paths, $prepend = false) + { + if (!$prefix) { + if ($prepend) { + $this->fallbackDirsPsr0 = array_merge( + (array) $paths, + $this->fallbackDirsPsr0 + ); + } else { + $this->fallbackDirsPsr0 = array_merge( + $this->fallbackDirsPsr0, + (array) $paths + ); + } + + return; + } + + $first = $prefix[0]; + if (!isset($this->prefixesPsr0[$first][$prefix])) { + $this->prefixesPsr0[$first][$prefix] = (array) $paths; + + return; + } + if ($prepend) { + $this->prefixesPsr0[$first][$prefix] = array_merge( + (array) $paths, + $this->prefixesPsr0[$first][$prefix] + ); + } else { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $this->prefixesPsr0[$first][$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, either + * appending or prepending to the ones previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-4 base directories + * @param bool $prepend Whether to prepend the directories + * + * @throws \InvalidArgumentException + */ + public function addPsr4($prefix, $paths, $prepend = false) + { + if (!$prefix) { + // Register directories for the root namespace. + if ($prepend) { + $this->fallbackDirsPsr4 = array_merge( + (array) $paths, + $this->fallbackDirsPsr4 + ); + } else { + $this->fallbackDirsPsr4 = array_merge( + $this->fallbackDirsPsr4, + (array) $paths + ); + } + } elseif (!isset($this->prefixDirsPsr4[$prefix])) { + // Register directories for a new namespace. + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } elseif ($prepend) { + // Prepend directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + (array) $paths, + $this->prefixDirsPsr4[$prefix] + ); + } else { + // Append directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $this->prefixDirsPsr4[$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, + * replacing any others previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 base directories + */ + public function set($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr0 = (array) $paths; + } else { + $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, + * replacing any others previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-4 base directories + * + * @throws \InvalidArgumentException + */ + public function setPsr4($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr4 = (array) $paths; + } else { + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } + } + + /** + * Turns on searching the include path for class files. + * + * @param bool $useIncludePath + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return bool + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Turns off searching the prefix and fallback directories for classes + * that have not been registered with the class map. + * + * @param bool $classMapAuthoritative + */ + public function setClassMapAuthoritative($classMapAuthoritative) + { + $this->classMapAuthoritative = $classMapAuthoritative; + } + + /** + * Should class lookup fail if not found in the current class map? + * + * @return bool + */ + public function isClassMapAuthoritative() + { + return $this->classMapAuthoritative; + } + + /** + * APCu prefix to use to cache found/not-found classes, if the extension is enabled. + * + * @param string|null $apcuPrefix + */ + public function setApcuPrefix($apcuPrefix) + { + $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null; + } + + /** + * The APCu prefix in use, or null if APCu caching is not enabled. + * + * @return string|null + */ + public function getApcuPrefix() + { + return $this->apcuPrefix; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + } + + /** + * Unregisters this instance as an autoloader. + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * @return bool|null True if loaded, null otherwise + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + includeFile($file); + + return true; + } + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|false The path if found, false otherwise + */ + public function findFile($class) + { + // class map lookup + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { + return false; + } + if (null !== $this->apcuPrefix) { + $file = apcu_fetch($this->apcuPrefix.$class, $hit); + if ($hit) { + return $file; + } + } + + $file = $this->findFileWithExtension($class, '.php'); + + // Search for Hack files if we are running on HHVM + if (false === $file && defined('HHVM_VERSION')) { + $file = $this->findFileWithExtension($class, '.hh'); + } + + if (null !== $this->apcuPrefix) { + apcu_add($this->apcuPrefix.$class, $file); + } + + if (false === $file) { + // Remember that this class does not exist. + $this->missingClasses[$class] = true; + } + + return $file; + } + + private function findFileWithExtension($class, $ext) + { + // PSR-4 lookup + $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; + + $first = $class[0]; + if (isset($this->prefixLengthsPsr4[$first])) { + $subPath = $class; + while (false !== $lastPos = strrpos($subPath, '\\')) { + $subPath = substr($subPath, 0, $lastPos); + $search = $subPath.'\\'; + if (isset($this->prefixDirsPsr4[$search])) { + $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); + foreach ($this->prefixDirsPsr4[$search] as $dir) { + if (file_exists($file = $dir . $pathEnd)) { + return $file; + } + } + } + } + } + + // PSR-4 fallback dirs + foreach ($this->fallbackDirsPsr4 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { + return $file; + } + } + + // PSR-0 lookup + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) + . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); + } else { + // PEAR-like class name + $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; + } + + if (isset($this->prefixesPsr0[$first])) { + foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + } + } + } + + // PSR-0 fallback dirs + foreach ($this->fallbackDirsPsr0 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + + // PSR-0 include paths. + if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { + return $file; + } + + return false; + } +} + +/** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + */ +function includeFile($file) +{ + include $file; +} diff --git a/plugins/langswitcher/vendor/composer/LICENSE b/plugins/langswitcher/vendor/composer/LICENSE new file mode 100644 index 0000000..f27399a --- /dev/null +++ b/plugins/langswitcher/vendor/composer/LICENSE @@ -0,0 +1,21 @@ + +Copyright (c) Nils Adermann, Jordi Boggiano + +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. + diff --git a/plugins/langswitcher/vendor/composer/autoload_classmap.php b/plugins/langswitcher/vendor/composer/autoload_classmap.php new file mode 100644 index 0000000..015272b --- /dev/null +++ b/plugins/langswitcher/vendor/composer/autoload_classmap.php @@ -0,0 +1,10 @@ + $baseDir . '/langswitcher.php', +); diff --git a/plugins/langswitcher/vendor/composer/autoload_namespaces.php b/plugins/langswitcher/vendor/composer/autoload_namespaces.php new file mode 100644 index 0000000..b7fc012 --- /dev/null +++ b/plugins/langswitcher/vendor/composer/autoload_namespaces.php @@ -0,0 +1,9 @@ += 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); + if ($useStaticLoader) { + require_once __DIR__ . '/autoload_static.php'; + + call_user_func(\Composer\Autoload\ComposerStaticInitc810fb4e9d827a69b99d6583ac14140c::getInitializer($loader)); + } else { + $map = require __DIR__ . '/autoload_namespaces.php'; + foreach ($map as $namespace => $path) { + $loader->set($namespace, $path); + } + + $map = require __DIR__ . '/autoload_psr4.php'; + foreach ($map as $namespace => $path) { + $loader->setPsr4($namespace, $path); + } + + $classMap = require __DIR__ . '/autoload_classmap.php'; + if ($classMap) { + $loader->addClassMap($classMap); + } + } + + $loader->register(true); + + return $loader; + } +} diff --git a/plugins/langswitcher/vendor/composer/autoload_static.php b/plugins/langswitcher/vendor/composer/autoload_static.php new file mode 100644 index 0000000..19ecc0a --- /dev/null +++ b/plugins/langswitcher/vendor/composer/autoload_static.php @@ -0,0 +1,20 @@ + __DIR__ . '/../..' . '/langswitcher.php', + ); + + public static function getInitializer(ClassLoader $loader) + { + return \Closure::bind(function () use ($loader) { + $loader->classMap = ComposerStaticInitc810fb4e9d827a69b99d6583ac14140c::$classMap; + + }, null, ClassLoader::class); + } +} diff --git a/plugins/langswitcher/vendor/composer/installed.json b/plugins/langswitcher/vendor/composer/installed.json new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/plugins/langswitcher/vendor/composer/installed.json @@ -0,0 +1 @@ +[]