FIX Correct Zend_Locale fallbacks in i18n/DateField/DateTimeField

Due to the recent change of translations to transifex, some
locales changed their names, which prompted a fix to
i18n::get_available_translations() (see 00ffe7294).
This caused a regression where short locales are determined
from the YAML file names (e.g. "en"), but weren't matched up
with fully qualified locales from get_available_translations() (e.g. "en_US").
Since this list is used in the admin/myprofile dropdown for the Member.Locale value,
it didn't match up with any entries and defaulted to the first one ("Africaans").

Note that the behaviour of admin/myprofile is still a bit weird:
It defaults the locale on new members to the one set for the current administrator.
So if a site defaults to en_US in _config.php, but the admin happens to view
his backend in de_DE, all members he creates default to de_DE as well.

Thanks to @tractorcow for contributing and peer reviewing!
This commit is contained in:
Ingo Schommer 2013-08-20 14:07:07 +02:00
parent c396645aed
commit 1c31c098ee
5 changed files with 30 additions and 28 deletions

View File

@ -217,6 +217,8 @@ class DateField extends TextField {
* @param String|Array $val * @param String|Array $val
*/ */
public function setValue($val) { public function setValue($val) {
$locale = new Zend_Locale($this->locale);
if(empty($val)) { if(empty($val)) {
$this->value = null; $this->value = null;
$this->valueObj = null; $this->valueObj = null;
@ -226,7 +228,7 @@ class DateField extends TextField {
if(is_array($val) && $this->validateArrayValue($val)) { if(is_array($val) && $this->validateArrayValue($val)) {
// set() gets confused with custom date formats when using array notation // set() gets confused with custom date formats when using array notation
if(!(empty($val['day']) || empty($val['month']) || empty($val['year']))) { if(!(empty($val['day']) || empty($val['month']) || empty($val['year']))) {
$this->valueObj = new Zend_Date($val, null, $this->locale); $this->valueObj = new Zend_Date($val, null, $locale);
$this->value = $this->valueObj->toArray(); $this->value = $this->valueObj->toArray();
} else { } else {
$this->value = $val; $this->value = $val;
@ -234,8 +236,8 @@ class DateField extends TextField {
} }
} }
// load ISO date from database (usually through Form->loadDataForm()) // load ISO date from database (usually through Form->loadDataForm())
else if(!empty($val) && Zend_Date::isDate($val, $this->getConfig('datavalueformat'), $this->locale)) { else if(!empty($val) && Zend_Date::isDate($val, $this->getConfig('datavalueformat'), $locale)) {
$this->valueObj = new Zend_Date($val, $this->getConfig('datavalueformat'), $this->locale); $this->valueObj = new Zend_Date($val, $this->getConfig('datavalueformat'), $locale);
$this->value = $this->valueObj->toArray(); $this->value = $this->valueObj->toArray();
} }
else { else {
@ -247,15 +249,15 @@ class DateField extends TextField {
// Caution: Its important to have this check *before* the ISO date fallback, // Caution: Its important to have this check *before* the ISO date fallback,
// as some dates are falsely detected as ISO by isDate(), e.g. '03/04/03' // as some dates are falsely detected as ISO by isDate(), e.g. '03/04/03'
// (en_NZ for 3rd of April, definetly not yyyy-MM-dd) // (en_NZ for 3rd of April, definetly not yyyy-MM-dd)
if(!empty($val) && Zend_Date::isDate($val, $this->getConfig('dateformat'), $this->locale)) { if(!empty($val) && Zend_Date::isDate($val, $this->getConfig('dateformat'), $locale)) {
$this->valueObj = new Zend_Date($val, $this->getConfig('dateformat'), $this->locale); $this->valueObj = new Zend_Date($val, $this->getConfig('dateformat'), $locale);
$this->value = $this->valueObj->get($this->getConfig('dateformat'), $this->locale); $this->value = $this->valueObj->get($this->getConfig('dateformat'), $locale);
} }
// load ISO date from database (usually through Form->loadDataForm()) // load ISO date from database (usually through Form->loadDataForm())
else if(!empty($val) && Zend_Date::isDate($val, $this->getConfig('datavalueformat'))) { else if(!empty($val) && Zend_Date::isDate($val, $this->getConfig('datavalueformat'))) {
$this->valueObj = new Zend_Date($val, $this->getConfig('datavalueformat')); $this->valueObj = new Zend_Date($val, $this->getConfig('datavalueformat'));
$this->value = $this->valueObj->get($this->getConfig('dateformat'), $this->locale); $this->value = $this->valueObj->get($this->getConfig('dateformat'), $locale);
} }
else { else {
$this->value = $val; $this->value = $val;

View File

@ -115,6 +115,8 @@ class DatetimeField extends FormField {
* the 'date' value may contain array notation was well (see {@link DateField->setValue()}). * the 'date' value may contain array notation was well (see {@link DateField->setValue()}).
*/ */
public function setValue($val) { public function setValue($val) {
$locale = new Zend_Locale($this->locale);
// If timezones are enabled, assume user data needs to be reverted to server timezone // If timezones are enabled, assume user data needs to be reverted to server timezone
if($this->getConfig('usertimezone')) { if($this->getConfig('usertimezone')) {
// Accept user input on timezone, but only when timezone support is enabled // Accept user input on timezone, but only when timezone support is enabled
@ -130,7 +132,7 @@ class DatetimeField extends FormField {
$this->timeField->setValue(null); $this->timeField->setValue(null);
} else { } else {
// Case 1: String setting from database, in ISO date format // Case 1: String setting from database, in ISO date format
if(is_string($val) && Zend_Date::isDate($val, $this->getConfig('datavalueformat'), $this->locale)) { if(is_string($val) && Zend_Date::isDate($val, $this->getConfig('datavalueformat'), $locale)) {
$this->value = $val; $this->value = $val;
} }
// Case 2: Array form submission with user date format // Case 2: Array form submission with user date format
@ -145,13 +147,13 @@ class DatetimeField extends FormField {
$this->dateField->setValue($val['date']); $this->dateField->setValue($val['date']);
$this->timeField->setValue($val['time']); $this->timeField->setValue($val['time']);
if($this->dateField->dataValue() && $this->timeField->dataValue()) { if($this->dateField->dataValue() && $this->timeField->dataValue()) {
$userValueObj = new Zend_Date(null, null, $this->locale); $userValueObj = new Zend_Date(null, null, $locale);
$userValueObj->setDate($this->dateField->dataValue(), $userValueObj->setDate($this->dateField->dataValue(),
$this->dateField->getConfig('datavalueformat')); $this->dateField->getConfig('datavalueformat'));
$userValueObj->setTime($this->timeField->dataValue(), $userValueObj->setTime($this->timeField->dataValue(),
$this->timeField->getConfig('datavalueformat')); $this->timeField->getConfig('datavalueformat'));
if($userTz) $userValueObj->setTimezone($dataTz); if($userTz) $userValueObj->setTimezone($dataTz);
$this->value = $userValueObj->get($this->getConfig('datavalueformat'), $this->locale); $this->value = $userValueObj->get($this->getConfig('datavalueformat'), $locale);
unset($userValueObj); unset($userValueObj);
} else { } else {
// Validation happens later, so set the raw string in case Zend_Date doesn't accept it // Validation happens later, so set the raw string in case Zend_Date doesn't accept it
@ -168,8 +170,8 @@ class DatetimeField extends FormField {
} }
// view settings (dates might differ from $this->value based on user timezone settings) // view settings (dates might differ from $this->value based on user timezone settings)
if (Zend_Date::isDate($this->value, $this->getConfig('datavalueformat'), $this->locale)) { if (Zend_Date::isDate($this->value, $this->getConfig('datavalueformat'), $locale)) {
$valueObj = new Zend_Date($this->value, $this->getConfig('datavalueformat'), $this->locale); $valueObj = new Zend_Date($this->value, $this->getConfig('datavalueformat'), $locale);
if($userTz) $valueObj->setTimezone($userTz); if($userTz) $valueObj->setTimezone($userTz);
// Set view values in sub-fields // Set view values in sub-fields
@ -177,9 +179,9 @@ class DatetimeField extends FormField {
$this->dateField->setValue($valueObj->toArray()); $this->dateField->setValue($valueObj->toArray());
} else { } else {
$this->dateField->setValue( $this->dateField->setValue(
$valueObj->get($this->dateField->getConfig('dateformat'), $this->locale)); $valueObj->get($this->dateField->getConfig('dateformat'), $locale));
} }
$this->timeField->setValue($valueObj->get($this->timeField->getConfig('timeformat'), $this->locale)); $this->timeField->setValue($valueObj->get($this->timeField->getConfig('timeformat'), $locale));
} }
} }

View File

@ -2231,13 +2231,11 @@ class i18n extends Object implements TemplateGlobalProvider {
// TODO Replace with CLDR list of actually available languages/regions // TODO Replace with CLDR list of actually available languages/regions
// Only allow explicitly registered locales, otherwise we'll get into trouble // Only allow explicitly registered locales, otherwise we'll get into trouble
// if the locale doesn't exist in Zend's CLDR data // if the locale doesn't exist in Zend's CLDR data
$labelLocale = str_replace('-', '_', self::get_locale_from_lang($locale)); $fullLocale = self::get_locale_from_lang($locale);
if(isset(self::$all_locales[$locale])) { if(isset($allLocales[$fullLocale])) {
$locales[$locale] = self::$all_locales[$locale]; $locales[$fullLocale] = $allLocales[$fullLocale];
} else if(isset(self::$all_locales[$labelLocale])) { }
$locales[$locale] = self::$all_locales[$labelLocale]; }
}
}
} }
} }
@ -2354,7 +2352,7 @@ class i18n extends Object implements TemplateGlobalProvider {
public static function get_locale_from_lang($lang) { public static function get_locale_from_lang($lang) {
$subtags = Config::inst()->get('i18n', 'likely_subtags'); $subtags = Config::inst()->get('i18n', 'likely_subtags');
if(preg_match('/\-|_/', $lang)) { if(preg_match('/\-|_/', $lang)) {
return $lang; return str_replace('-', '_', $lang);
} else if(isset($subtags[$lang])) { } else if(isset($subtags[$lang])) {
return $subtags[$lang]; return $subtags[$lang];
} else { } else {

View File

@ -1169,7 +1169,7 @@ class Member extends DataObject implements TemplateGlobalProvider {
$password->setCanBeEmpty(true); $password->setCanBeEmpty(true);
if(!$this->ID) $password->showOnClick = false; if(!$this->ID) $password->showOnClick = false;
$mainFields->replaceField('Password', $password); $mainFields->replaceField('Password', $password);
$mainFields->replaceField('Locale', new DropdownField( $mainFields->replaceField('Locale', new DropdownField(
"Locale", "Locale",
_t('Member.INTERFACELANG', "Interface Language", 'Language of the CMS'), _t('Member.INTERFACELANG', "Interface Language", 'Language of the CMS'),
@ -1231,7 +1231,7 @@ class Member extends DataObject implements TemplateGlobalProvider {
$permissionsTab = $fields->fieldByName("Root")->fieldByName('Permissions'); $permissionsTab = $fields->fieldByName("Root")->fieldByName('Permissions');
if($permissionsTab) $permissionsTab->addExtraClass('readonly'); if($permissionsTab) $permissionsTab->addExtraClass('readonly');
$defaultDateFormat = Zend_Locale_Format::getDateFormat($this->Locale); $defaultDateFormat = Zend_Locale_Format::getDateFormat(new Zend_Locale($this->Locale));
$dateFormatMap = array( $dateFormatMap = array(
'MMM d, yyyy' => Zend_Date::now()->toString('MMM d, yyyy'), 'MMM d, yyyy' => Zend_Date::now()->toString('MMM d, yyyy'),
'yyyy/MM/dd' => Zend_Date::now()->toString('yyyy/MM/dd'), 'yyyy/MM/dd' => Zend_Date::now()->toString('yyyy/MM/dd'),
@ -1249,7 +1249,7 @@ class Member extends DataObject implements TemplateGlobalProvider {
); );
$dateFormatField->setValue($this->DateFormat); $dateFormatField->setValue($this->DateFormat);
$defaultTimeFormat = Zend_Locale_Format::getTimeFormat($this->Locale); $defaultTimeFormat = Zend_Locale_Format::getTimeFormat(new Zend_Locale($this->Locale));
$timeFormatMap = array( $timeFormatMap = array(
'h:mm a' => Zend_Date::now()->toString('h:mm a'), 'h:mm a' => Zend_Date::now()->toString('h:mm a'),
'H:mm' => Zend_Date::now()->toString('H:mm'), 'H:mm' => Zend_Date::now()->toString('H:mm'),

View File

@ -97,9 +97,9 @@ class i18nTest extends SapphireTest {
public function testGetExistingTranslations() { public function testGetExistingTranslations() {
$translations = i18n::get_existing_translations(); $translations = i18n::get_existing_translations();
$this->assertTrue(isset($translations['en']), 'Checking for en translation'); $this->assertTrue(isset($translations['en_US']), 'Checking for en translation');
$this->assertEquals($translations['en'], 'English (United States)'); $this->assertEquals($translations['en_US'], 'English (United States)');
$this->assertTrue(isset($translations['de']), 'Checking for de_DE translation'); $this->assertTrue(isset($translations['de_DE']), 'Checking for de_DE translation');
} }
public function testDataObjectFieldLabels() { public function testDataObjectFieldLabels() {