1
0
mirror of https://github.com/processwire/processwire.git synced 2025-08-09 16:26:59 +02:00

Updates for issue processwire/processwire-issues#215 to better support locale settings on front-end, plus add $languages->setLocale() and $languages->getLocale() methods, and freshen up code and docs in related classes.

This commit is contained in:
Ryan Cramer
2017-03-16 09:49:47 -04:00
parent e1928c9e3c
commit 9c92ce5305
4 changed files with 205 additions and 14 deletions

View File

@@ -391,6 +391,7 @@ class Selectors extends WireArray {
$operator = $op; $operator = $op;
$not = true; $not = true;
} else { } else {
if(is_array($value)) $value = implode('|', $value);
$debug = $this->wire('config')->debug ? "field='$field', value='$value', selector: '$this->selectorStr'" : ""; $debug = $this->wire('config')->debug ? "field='$field', value='$value', selector: '$this->selectorStr'" : "";
throw new WireException("Unknown Selector operator: '$operator' -- was your selector value properly escaped? $debug"); throw new WireException("Unknown Selector operator: '$operator' -- was your selector value properly escaped? $debug");
} }
@@ -434,7 +435,7 @@ class Selectors extends WireArray {
} }
} }
if($field || strlen("$value")) { if($field || $value || strlen("$value")) {
$selector = $this->create($field, $operator, $value); $selector = $this->create($field, $operator, $value);
if(!is_null($group)) $selector->group = $group; if(!is_null($group)) $selector->group = $group;
if($quote) $selector->quote = $quote; if($quote) $selector->quote = $quote;

View File

@@ -182,8 +182,8 @@ class LanguageSupport extends WireData implements Module, ConfigurableModule {
} }
$this->wire('config')->dateFormat = $this->_('Y-m-d H:i:s'); // Sortable date format used in the admin $this->wire('config')->dateFormat = $this->_('Y-m-d H:i:s'); // Sortable date format used in the admin
$locale = $this->_('C'); // Value to pass to PHP's setlocale(LC_ALL, 'value') function when initializing this language // Default is 'C'. Specify '0' to skip the setlocale() call (and carry on system default). $locale = $this->_('C'); // Value to pass to PHP's setlocale(LC_ALL, 'value') function when initializing this language // Default is 'C'. Specify '0' to skip the setlocale() call (and carry on system default). Specify CSV string of locales to try multiple locales in order.
if($locale != '0') setlocale(LC_ALL, $locale); if($locale != '0') $languages->setLocale(LC_ALL, $locale);
// setup our hooks handled by this class // setup our hooks handled by this class
$this->addHookBefore('Inputfield::render', $this, 'hookInputfieldBeforeRender'); $this->addHookBefore('Inputfield::render', $this, 'hookInputfieldBeforeRender');

View File

@@ -3,8 +3,12 @@
/** /**
* Multi-language support page names module * Multi-language support page names module
* *
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer * ProcessWire 3.x, Copyright 2017 by Ryan Cramer
* https://processwire.com * https://processwire.com
*
* @property int $moduleVersion
* @property int $inheritInactive
* @property int $useHomeSegment
* *
*/ */
@@ -129,7 +133,10 @@ class LanguageSupportPageNames extends WireData implements Module, ConfigurableM
// verify that page path doesn't have mixed languages where it shouldn't // verify that page path doesn't have mixed languages where it shouldn't
$redirectURL = $this->verifyPath($this->requestPath); $redirectURL = $this->verifyPath($this->requestPath);
if($redirectURL) return $this->wire('session')->redirect($redirectURL); if($redirectURL) {
$this->wire('session')->redirect($redirectURL);
return;
}
$page = $this->wire('page'); $page = $this->wire('page');
@@ -178,6 +185,9 @@ class LanguageSupportPageNames extends WireData implements Module, ConfigurableM
* Given a page path, return an updated version that lacks the language segment * Given a page path, return an updated version that lacks the language segment
* *
* It extracts the language segment and uses that to later set the language * It extracts the language segment and uses that to later set the language
*
* @param string $path
* @return string
* *
*/ */
public function updatePath($path) { public function updatePath($path) {
@@ -269,7 +279,8 @@ class LanguageSupportPageNames extends WireData implements Module, ConfigurableM
// set the language // set the language
if(!$setLanguage) $setLanguage = $languages->get('default'); if(!$setLanguage) $setLanguage = $languages->get('default');
$user->language = $setLanguage; $user->language = $setLanguage;
$this->setLanguage = $setLanguage; $this->setLanguage = $setLanguage;
$languages->setLocale();
// if $page is the 404 page, exit out now // if $page is the 404 page, exit out now
if($page->id == $config->http404PageID) return ''; if($page->id == $config->http404PageID) return '';
@@ -360,10 +371,14 @@ class LanguageSupportPageNames extends WireData implements Module, ConfigurableM
/** /**
* Hook in before ProcesssPageView::execute to capture and modify $_GET[it] as needed * Hook in before ProcesssPageView::execute to capture and modify $_GET[it] as needed
*
* @param HookEvent $event
* *
*/ */
public function hookProcessPageViewExecute(HookEvent $event) { public function hookProcessPageViewExecute(HookEvent $event) {
$event->object->setDelayRedirects(true); /** @var ProcessPageView $process */
$process = $event->object;
$process->setDelayRedirects(true);
// save now, since ProcessPageView removes $_GET['it'] when it executes // save now, since ProcessPageView removes $_GET['it'] when it executes
$it = isset($_GET['it']) ? $_GET['it'] : ''; $it = isset($_GET['it']) ? $_GET['it'] : '';
$this->requestPath = $it; $this->requestPath = $it;
@@ -377,9 +392,13 @@ class LanguageSupportPageNames extends WireData implements Module, ConfigurableM
/** /**
* Hook in before ProcesssPageView::render to throw 404 when appropriate * Hook in before ProcesssPageView::render to throw 404 when appropriate
*
* @param HookEvent $event
* @throws WireException
* *
*/ */
public function hookPageRender(HookEvent $event) { public function hookPageRender(HookEvent $event) {
if($event) {}
if($this->force404) { if($this->force404) {
$this->force404 = false; // prevent another 404 on the 404 page $this->force404 = false; // prevent another 404 on the 404 page
throw new Wire404Exception(); throw new Wire404Exception();
@@ -390,12 +409,16 @@ class LanguageSupportPageNames extends WireData implements Module, ConfigurableM
* Hook in after ProcesssPageView::viewable account for specific language versions * Hook in after ProcesssPageView::viewable account for specific language versions
* *
* May be passed a Language name or page to check viewable for that language * May be passed a Language name or page to check viewable for that language
*
* @param HookEvent $event
* *
*/ */
public function hookPageViewable(HookEvent $event) { public function hookPageViewable(HookEvent $event) {
if(!$event->return) return; if(!$event->return) return;
/** @var Page $page */
$page = $event->object; $page = $event->object;
// if(wire('user')->isSuperuser() || $page->editable()) return; // if(wire('user')->isSuperuser() || $page->editable()) return;
/** @var Language $language */
$language = $event->arguments(0); $language = $event->arguments(0);
if(!$language) return; if(!$language) return;
if(is_string($language)) $language = $this->wire('languages')->get($this->wire('sanitizer')->pageNameUTF8($language)); if(is_string($language)) $language = $this->wire('languages')->get($this->wire('sanitizer')->pageNameUTF8($language));
@@ -407,10 +430,14 @@ class LanguageSupportPageNames extends WireData implements Module, ConfigurableM
/** /**
* Hook into WirePageEditor (i.e. ProcessPageEdit) to remove the non-applicable default home name of 'home' * Hook into WirePageEditor (i.e. ProcessPageEdit) to remove the non-applicable default home name of 'home'
*
* @param HookEvent $event
* *
*/ */
public function hookWirePageEditorExecute(HookEvent $event) { public function hookWirePageEditorExecute(HookEvent $event) {
$page = $event->object->getPage(); /** @var WirePageEditor $editor */
$editor = $event->object;
$page = $editor->getPage();
if($page && $page->id == 1) { if($page && $page->id == 1) {
if($page->name == Pages::defaultRootName) $page->name = ''; if($page->name == Pages::defaultRootName) $page->name = '';
} }
@@ -420,6 +447,8 @@ class LanguageSupportPageNames extends WireData implements Module, ConfigurableM
* Hook into the page name render for when in ProcessPageEdit * Hook into the page name render for when in ProcessPageEdit
* *
* Adds additional inputs for each language * Adds additional inputs for each language
*
* @param HookEvent $event
* *
*/ */
public function hookInputfieldPageNameRenderAfter(HookEvent $event) { public function hookInputfieldPageNameRenderAfter(HookEvent $event) {
@@ -489,13 +518,19 @@ class LanguageSupportPageNames extends WireData implements Module, ConfigurableM
* Process the input data from hookInputfieldPageNameRender * Process the input data from hookInputfieldPageNameRender
* *
* @todo Just move this to the InputfieldPageName module rather than using hooks * @todo Just move this to the InputfieldPageName module rather than using hooks
*
* @param HookEvent $event
* *
*/ */
public function hookInputfieldPageNameProcess(HookEvent $event) { public function hookInputfieldPageNameProcess(HookEvent $event) {
/** @var InputfieldPageName $inputfield */
$inputfield = $event->object; $inputfield = $event->object;
//$page = $this->process == 'ProcessPageEdit' ? $this->process->getPage() : new NullPage(); //$page = $this->process == 'ProcessPageEdit' ? $this->process->getPage() : new NullPage();
$page = $this->process->getPage(); /** @var WirePageEditor $process */
$process = $this->process;
/** @var Page $page */
$page = $process instanceof WirePageEditor ? $process->getPage() : new NullPage();
if($page->id && !$page->editable('name', false)) return; // name is not editable if($page->id && !$page->editable('name', false)) return; // name is not editable
$input = $event->arguments[0]; $input = $event->arguments[0];
/** @var Languages $languages */ /** @var Languages $languages */
@@ -533,10 +568,13 @@ class LanguageSupportPageNames extends WireData implements Module, ConfigurableM
/** /**
* Hook into PageFinder::getQuery to add language status check * Hook into PageFinder::getQuery to add language status check
*
* @param HookEvent $event
* *
*/ */
public function hookPageFinderGetQuery(HookEvent $event) { public function hookPageFinderGetQuery(HookEvent $event) {
$query = $event->return; $query = $event->return;
/** @var PageFinder $pageFinder */
$pageFinder = $event->object; $pageFinder = $event->object;
$options = $pageFinder->getOptions(); $options = $pageFinder->getOptions();
@@ -555,9 +593,12 @@ class LanguageSupportPageNames extends WireData implements Module, ConfigurableM
/** /**
* Hook into Page::path to localize path for current language * Hook into Page::path to localize path for current language
*
* @param HookEvent $event
* *
*/ */
public function hookPagePath(HookEvent $event) { public function hookPagePath(HookEvent $event) {
/** @var Page $page */
$page = $event->object; $page = $event->object;
if($page->template == 'admin') return; if($page->template == 'admin') return;
$language = $this->wire('user')->get('language'); $language = $this->wire('user')->get('language');
@@ -570,6 +611,8 @@ class LanguageSupportPageNames extends WireData implements Module, ConfigurableM
* *
* event param Language|string|int Optional language * event param Language|string|int Optional language
* event return string Localized language name or blank if not set * event return string Localized language name or blank if not set
*
* @param HookEvent $event
* *
*/ */
public function hookPageLocalName(HookEvent $event) { public function hookPageLocalName(HookEvent $event) {
@@ -586,9 +629,12 @@ class LanguageSupportPageNames extends WireData implements Module, ConfigurableM
* *
* event param Language|string|int Optional language * event param Language|string|int Optional language
* event return string Localized language path * event return string Localized language path
*
* @param HookEvent $event
* *
*/ */
public function hookPageLocalPath(HookEvent $event) { public function hookPageLocalPath(HookEvent $event) {
/** @var Page $page */
$page = $event->object; $page = $event->object;
$language = $this->getLanguage($event->arguments(0)); $language = $this->getLanguage($event->arguments(0));
$event->return = $this->getPagePath($page, $language); $event->return = $this->getPagePath($page, $language);
@@ -599,9 +645,12 @@ class LanguageSupportPageNames extends WireData implements Module, ConfigurableM
* *
* event param Language|string|int Optional language * event param Language|string|int Optional language
* event return string Localized language URL * event return string Localized language URL
*
* @param HookEvent $event
* *
*/ */
public function hookPageLocalUrl(HookEvent $event) { public function hookPageLocalUrl(HookEvent $event) {
/** @var Page $page */
$page = $event->object; $page = $event->object;
$language = $this->getLanguage($event->arguments(0)); $language = $this->getLanguage($event->arguments(0));
$event->return = $this->wire('config')->urls->root . ltrim($this->getPagePath($page, $language), '/'); $event->return = $this->wire('config')->urls->root . ltrim($this->getPagePath($page, $language), '/');
@@ -612,6 +661,8 @@ class LanguageSupportPageNames extends WireData implements Module, ConfigurableM
* *
* event param Language|string|int Optional language * event param Language|string|int Optional language
* event return string Localized language name or blank if not set * event return string Localized language name or blank if not set
*
* @param HookEvent $event
* *
*/ */
public function hookPageLocalHttpUrl(HookEvent $event) { public function hookPageLocalHttpUrl(HookEvent $event) {
@@ -649,6 +700,8 @@ class LanguageSupportPageNames extends WireData implements Module, ConfigurableM
/** /**
* Update pages table for new column when a language is added * Update pages table for new column when a language is added
*
* @param Language|Page $language
* *
*/ */
public function languageAdded(Page $language) { public function languageAdded(Page $language) {
@@ -668,6 +721,8 @@ class LanguageSupportPageNames extends WireData implements Module, ConfigurableM
/** /**
* Hook called when language is added * Hook called when language is added
*
* @param HookEvent $event
* *
*/ */
public function hookLanguageAdded(HookEvent $event) { public function hookLanguageAdded(HookEvent $event) {
@@ -677,6 +732,8 @@ class LanguageSupportPageNames extends WireData implements Module, ConfigurableM
/** /**
* Update pages table to remove column when a language is deleted * Update pages table to remove column when a language is deleted
*
* @param Language|Page $language
* *
*/ */
protected function languageDeleted(Page $language) { protected function languageDeleted(Page $language) {
@@ -695,6 +752,8 @@ class LanguageSupportPageNames extends WireData implements Module, ConfigurableM
/** /**
* Hook called when language is deleted * Hook called when language is deleted
*
* @param HookEvent $event
* *
*/ */
public function hookLanguageDeleted(HookEvent $event) { public function hookLanguageDeleted(HookEvent $event) {
@@ -707,6 +766,8 @@ class LanguageSupportPageNames extends WireData implements Module, ConfigurableM
* *
* Here we make use of the 'extraData' return property of the saveReady hook * Here we make use of the 'extraData' return property of the saveReady hook
* to bundle in the language name fields into the query. * to bundle in the language name fields into the query.
*
* @param HookEvent $event
* *
*/ */
public function hookPageSaveReady(HookEvent $event) { public function hookPageSaveReady(HookEvent $event) {
@@ -781,7 +842,8 @@ class LanguageSupportPageNames extends WireData implements Module, ConfigurableM
* *
*/ */
public function hookPageSetupNew(HookEvent $event) { public function hookPageSetupNew(HookEvent $event) {
/** @var Page $page */
$page = $event->arguments[0]; $page = $event->arguments[0];
// if page already has a name, then no need to continue // if page already has a name, then no need to continue
@@ -821,6 +883,8 @@ class LanguageSupportPageNames extends WireData implements Module, ConfigurableM
/** /**
* Hook called immediately after a page is saved * Hook called immediately after a page is saved
* *
* @param HookEvent $event
*
*/ */
public function hookPageSaved(HookEvent $event) { public function hookPageSaved(HookEvent $event) {
// The setLanguage may get lost upon some page save events, so this restores that // The setLanguage may get lost upon some page save events, so this restores that
@@ -925,6 +989,8 @@ class LanguageSupportPageNames extends WireData implements Module, ConfigurableM
/** /**
* Check to make sure that the status table exists and creates it if not * Check to make sure that the status table exists and creates it if not
*
* @param bool $force
* *
*/ */
public function checkModuleVersion($force = false) { public function checkModuleVersion($force = false) {
@@ -961,6 +1027,9 @@ class LanguageSupportPageNames extends WireData implements Module, ConfigurableM
/** /**
* Module interactive configuration fields * Module interactive configuration fields
*
* @param array $data
* @return InputfieldWrapper
* *
*/ */
public function getModuleConfigInputfields(array $data) { public function getModuleConfigInputfields(array $data) {

View File

@@ -29,6 +29,7 @@
* @method added(Page $language) Hook called when Language is added #pw-hooker * @method added(Page $language) Hook called when Language is added #pw-hooker
* @method deleted(Page $language) Hook called when Language is deleted #pw-hooker * @method deleted(Page $language) Hook called when Language is deleted #pw-hooker
* @method updated(Page $language, $what) Hook called when Language is added or deleted #pw-hooker * @method updated(Page $language, $what) Hook called when Language is added or deleted #pw-hooker
* @method languageChanged($fromLanguage, $toLanguage) Hook called when User language is changed #pw-hooker
* *
*/ */
@@ -36,6 +37,8 @@ class Languages extends PagesType {
/** /**
* Reference to LanguageTranslator instance * Reference to LanguageTranslator instance
*
* @var LanguageTranslator
* *
*/ */
protected $translator = null; protected $translator = null;
@@ -93,7 +96,15 @@ class Languages extends PagesType {
* *
*/ */
protected $editableCache = array(); protected $editableCache = array();
/**
* Construct
*
* @param ProcessWire $wire
* @param array $templates
* @param array $parents
*
*/
public function __construct(ProcessWire $wire, $templates = array(), $parents = array()) { public function __construct(ProcessWire $wire, $templates = array(), $parents = array()) {
parent::__construct($wire, $templates, $parents); parent::__construct($wire, $templates, $parents);
$this->wire('database')->addHookAfter('unknownColumnError', $this, 'hookUnknownColumnError'); $this->wire('database')->addHookAfter('unknownColumnError', $this, 'hookUnknownColumnError');
@@ -107,9 +118,15 @@ class Languages extends PagesType {
* *
*/ */
public function translator(Language $language) { public function translator(Language $language) {
if(is_null($this->translator)) $this->translator = $this->wire(new LanguageTranslator($language)); /** @var LanguageTranslator $translator */
else $this->translator->setCurrentLanguage($language); $translator = $this->translator;
return $this->translator; if(is_null($translator)) {
$translator = $this->wire(new LanguageTranslator($language));
$this->translator = $translator;
} else {
$translator->setCurrentLanguage($language);
}
return $translator;
} }
/** /**
@@ -348,6 +365,110 @@ class Languages extends PagesType {
$user->language = $this->savedLanguage2; $user->language = $this->savedLanguage2;
return true; return true;
} }
/**
* Set the current locale
*
* This function behaves exactly the same way as [PHP setlocale](http://php.net/manual/en/function.setlocale.php) except
* for the following:
*
* - If the $locale argument is omitted, it uses the locale setting translated for the current user language.
* - You can optionally specify a CSV string of locales to try for the $locale argument.
* - You can optionally or a “category=locale;category=locale;category=locale” string for the $locale argument.
* When this type of string is used, the $category argument is ignored.
* - This method does not accept more than the 2 indicated arguments.
*
* See the PHP setlocale link above for a list of constants that can be used for the `$category` argument.
*
* ~~~~~
* // Set locale to whatever settings defined for current $user language
* $languages->setLocale();
*
* // Set all locale categories
* $languages->setLocale(LC_ALL, 'en_US.UTF-8');
*
* // Set locale for specific category (CTYPE)
* $langauges->setLocale(LC_CTYPE, 'en_US.UTF-8');
*
* // Try multiple locales till one works (in order) using array
* $languages->setLocale(LC_ALL, [ 'en_US.UTF-8', 'en_US', 'en' ]);
*
* // Same as above, except using CSV string
* $languages->setLocale(LC_ALL, 'en_US.UTF-8, en_US, en');
*
* // Set multiple categories and locales (first argument ignored)
* $languages->setLocale(null, 'LC_CTYPE=en_US;LC_NUMERIC=de_DE;LC_TIME=es_ES');
* ~~~~~
*
* @param int $category Specify a PHP “LC_” constant or omit (or null) for default (LC_ALL).
* @param string|array|null $locale Specify string, array or CSV string of locale name(s), or omit (null) for default language locale.
* @return string|bool Returns the locale that was set or boolean false if requested locale cannot be set.
* @see Languages::getLocale()
*
*/
public function setLocale($category = LC_ALL, $locale = null) {
$setLocale = ''; // return value
if($category === null) $category = LC_ALL;
if($locale === null) {
// argument omitted means set according to language settings
$locale = __('C', 'wire--modules--languagesupport--languagesupport-module');
}
if(is_string($locale)) {
if(strpos($locale, ',') !== false) {
// convert CSV string to array of locales
$locale = explode(',', $locale);
foreach($locale as $key => $value) {
$locale[$key] = trim($value);
}
} else if(strpos($locale, ';') !== false) {
// multi-category and locale string, i.e. LC_CTYPE=en_US.UTF-8;LC_NUMERIC=C;LC_TIME=C
foreach(explode(';', $locale) as $s) {
// call setLocale() for each locale item present in the string
if(strpos($s, '=') === false) continue;
list($cats, $loc) = explode('=', $s);
$cat = constant($cats);
if($cat !== null) {
$loc = $this->setLocale($cat, $loc);
if($loc !== false) $setLocale .= trim($cats) . '=' . trim($loc) . ";";
}
}
$setLocale = rtrim($setLocale, ';');
if(empty($setLocale)) $setLocale = false;
}
}
if($setLocale === '') {
if($locale === '0' || $locale === 0) {
// get locale (to be consistent with behavior of PHP setlocale)
$setLocale = $this->getLocale($category);
} else {
// set the locale
$setLocale = setlocale($category, $locale);
}
}
return $setLocale;
}
/**
* Return the current locale setting
*
* If using LC_ALL category and locales change by category, the returned string will be in
* the format: “category=locale;category=locale”, and so on.
*
* @param int $category Optionally specify a PHP LC constant (default=LC_ALL)
* @return string|bool Locale(s) string or boolean false if not supported by the system.
* @see Languages::setLocale()
*
*/
public function getLocale($category = LC_ALL) {
return setlocale($category, '0');
}
/** /**
* Hook called when a language is deleted * Hook called when a language is deleted