Index: includes/form.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/form.inc,v
retrieving revision 1.502
diff -u -p -r1.502 form.inc
--- includes/form.inc	7 Oct 2010 17:31:39 -0000	1.502
+++ includes/form.inc	7 Oct 2010 18:29:59 -0000
@@ -1230,8 +1230,14 @@ function _form_validate(&$elements, &$fo
         // case there is no #title, we cannot set a form error message.
         // Instead of setting no #title, form constructors are encouraged to set
         // #title_display to 'invisible' to improve accessibility.
-        if (isset($elements['#title'])) {
-          form_error($elements, $t('!name field is required.', array('!name' => $elements['#title'])));
+        $t_args = array(
+          '!title' => isset($elements['#title']) ? $elements['#title'] : '',
+        );
+        if (!empty($elements['#required_message'])) {
+          form_error($elements, $t($elements['#required_message'], $t_args));
+        }
+        elseif (isset($elements['#title'])) {
+          form_error($elements, $t('!title field is required.', $t_args));
         }
         else {
           form_error($elements);
@@ -1860,7 +1866,7 @@ function _form_builder_handle_input_elem
       // Avoid image buttons (which come with garbage value), so we only get value
       // for the button actually clicked.
       if (!isset($element['#value']) && empty($element['#has_garbage_value'])) {
-        $element['#value'] = isset($element['#default_value']) ? $element['#default_value'] : '';
+        $element['#value'] = array_key_exists('#default_value', $element) ? $element['#default_value'] : '';
       }
     }
   }
Index: modules/field/tests/field.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/tests/field.test,v
retrieving revision 1.42
diff -u -p -r1.42 field.test
--- modules/field/tests/field.test	28 Sep 2010 02:30:31 -0000	1.42
+++ modules/field/tests/field.test	7 Oct 2010 18:29:59 -0000
@@ -1300,7 +1300,7 @@ class FieldFormTestCase extends FieldTes
     // Submit with missing required value.
     $edit = array();
     $this->drupalPost('test-entity/add/test-bundle', $edit, t('Save'));
-    $this->assertRaw(t('!name field is required.', array('!name' => $this->instance['label'])), 'Required field with no value fails validation');
+    $this->assertRaw(t('!title field is required.', array('!title' => $this->instance['label'])), 'Required field with no value fails validation');
 
     // Create an entity
     $value = mt_rand(1, 127);
@@ -1316,7 +1316,7 @@ class FieldFormTestCase extends FieldTes
     $value = '';
     $edit = array("{$this->field_name}[$langcode][0][value]" => $value);
     $this->drupalPost('test-entity/manage/' . $id . '/edit', $edit, t('Save'));
-    $this->assertRaw(t('!name field is required.', array('!name' => $this->instance['label'])), 'Required field with no value fails validation');
+    $this->assertRaw(t('!title field is required.', array('!title' => $this->instance['label'])), 'Required field with no value fails validation');
   }
 
 //  function testFieldFormMultiple() {
Index: modules/file/file.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/file/file.module,v
retrieving revision 1.42
diff -u -p -r1.42 file.module
--- modules/file/file.module	4 Oct 2010 17:46:01 -0000	1.42
+++ modules/file/file.module	7 Oct 2010 18:29:59 -0000
@@ -563,7 +563,7 @@ function file_managed_file_validate(&$el
 
   // Check required property based on the FID.
   if ($element['#required'] && empty($element['fid']['#value']) && !in_array($clicked_button, array('upload_button', 'remove_button'))) {
-    form_error($element['upload'], t('!name field is required.', array('!name' => $element['#title'])));
+    form_error($element['upload'], t('!title field is required.', array('!title' => $element['#title'])));
   }
 
   // Consolidate the array value of this field to a single FID.
Index: modules/simpletest/tests/form.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/form.test,v
retrieving revision 1.72
diff -u -p -r1.72 form.test
--- modules/simpletest/tests/form.test	4 Oct 2010 18:00:46 -0000	1.72
+++ modules/simpletest/tests/form.test	7 Oct 2010 18:30:44 -0000
@@ -123,6 +123,22 @@ class FormsTestCase extends DrupalWebTes
   }
 
   /**
+   * Tests #required with custom validation errors.
+   *
+   * @see form_test_validate_required_form()
+   */
+  function testRequiredValidation() {
+    $this->drupalPost('form-test/validate-required', array(), 'Submit');
+
+    // Verify that the #required error message can be overridden.
+    foreach (array('Textfield', 'Textarea', 'Checkboxes', 'Radios') as $title) {
+      $this->assertNoText(t('!title field is required.', array('!title' => $title)));
+      $this->assertText(strtr('Yo, ur !title iz amptee!', array('!title' => $title)));
+    }
+    $this->assertNoText(t('An illegal choice has been detected. Please contact the site administrator.'));
+  }
+
+  /**
    * Test default value handling for checkboxes.
    *
    * @see _form_test_checkbox()
@@ -131,7 +147,7 @@ class FormsTestCase extends DrupalWebTes
     // First, try to submit without the required checkbox.
     $edit = array();
     $this->drupalPost('form-test/checkbox', $edit, t('Submit'));
-    $this->assertRaw(t('!name field is required.', array('!name' => 'required_checkbox')), t('A required checkbox is actually mandatory'));
+    $this->assertRaw(t('!title field is required.', array('!title' => 'required_checkbox')), t('A required checkbox is actually mandatory'));
 
     // Now try to submit the form correctly.
     $values = drupal_json_decode($this->drupalPost(NULL, array('required_checkbox' => 1), t('Submit')));
@@ -158,26 +174,26 @@ class FormsTestCase extends DrupalWebTes
   function testSelect() {
     $form = $form_state = array();
     $form = form_test_select($form, $form_state);
-    $error = '!name field is required.';
+    $error = '!title field is required.';
     $this->drupalGet('form-test/select');
 
     // Posting without any values should throw validation errors.
     $this->drupalPost(NULL, array(), 'Submit');
-    $this->assertNoText(t($error, array('!name' => $form['select']['#title'])));
-    $this->assertNoText(t($error, array('!name' => $form['select_required']['#title'])));
-    $this->assertNoText(t($error, array('!name' => $form['select_optional']['#title'])));
-    $this->assertNoText(t($error, array('!name' => $form['empty_value']['#title'])));
-    $this->assertNoText(t($error, array('!name' => $form['empty_value_one']['#title'])));
-    $this->assertText(t($error, array('!name' => $form['no_default']['#title'])));
-    $this->assertNoText(t($error, array('!name' => $form['no_default_optional']['#title'])));
-    $this->assertText(t($error, array('!name' => $form['no_default_empty_option']['#title'])));
-    $this->assertNoText(t($error, array('!name' => $form['no_default_empty_option_optional']['#title'])));
-    $this->assertText(t($error, array('!name' => $form['no_default_empty_value']['#title'])));
-    $this->assertText(t($error, array('!name' => $form['no_default_empty_value_one']['#title'])));
-    $this->assertNoText(t($error, array('!name' => $form['no_default_empty_value_optional']['#title'])));
-    $this->assertNoText(t($error, array('!name' => $form['multiple']['#title'])));
-    $this->assertNoText(t($error, array('!name' => $form['multiple_no_default']['#title'])));
-    $this->assertText(t($error, array('!name' => $form['multiple_no_default_required']['#title'])));
+    $this->assertNoText(t($error, array('!title' => $form['select']['#title'])));
+    $this->assertNoText(t($error, array('!title' => $form['select_required']['#title'])));
+    $this->assertNoText(t($error, array('!title' => $form['select_optional']['#title'])));
+    $this->assertNoText(t($error, array('!title' => $form['empty_value']['#title'])));
+    $this->assertNoText(t($error, array('!title' => $form['empty_value_one']['#title'])));
+    $this->assertText(t($error, array('!title' => $form['no_default']['#title'])));
+    $this->assertNoText(t($error, array('!title' => $form['no_default_optional']['#title'])));
+    $this->assertText(t($error, array('!title' => $form['no_default_empty_option']['#title'])));
+    $this->assertNoText(t($error, array('!title' => $form['no_default_empty_option_optional']['#title'])));
+    $this->assertText(t($error, array('!title' => $form['no_default_empty_value']['#title'])));
+    $this->assertText(t($error, array('!title' => $form['no_default_empty_value_one']['#title'])));
+    $this->assertNoText(t($error, array('!title' => $form['no_default_empty_value_optional']['#title'])));
+    $this->assertNoText(t($error, array('!title' => $form['multiple']['#title'])));
+    $this->assertNoText(t($error, array('!title' => $form['multiple_no_default']['#title'])));
+    $this->assertText(t($error, array('!title' => $form['multiple_no_default_required']['#title'])));
 
     // Post values for required fields.
     $edit = array(
@@ -468,7 +484,7 @@ class FormValidationTestCase extends Dru
     // ensure that the title field is not validated, but the #element_validate
     // handler for the 'test' field is triggered.
     $this->drupalPost($path, $edit, t('Partial validate'));
-    $this->assertNoText(t('!name field is required.', array('!name' => 'Title')));
+    $this->assertNoText(t('!title field is required.', array('!title' => 'Title')));
     $this->assertText('Test element is invalid');
 
     // Ensure not validated values are not available to submit handlers.
@@ -478,7 +494,7 @@ class FormValidationTestCase extends Dru
     // Now test full form validation and ensure that the #element_validate
     // handler is still triggered.
     $this->drupalPost($path, $edit, t('Full validate'));
-    $this->assertText(t('!name field is required.', array('!name' => 'Title')));
+    $this->assertText(t('!title field is required.', array('!title' => 'Title')));
     $this->assertText('Test element is invalid');
   }
 }
@@ -895,7 +911,7 @@ class FormsFormStorageTestCase extends D
 
       // Test it again, but first trigger a validation error, then test.
       $this->drupalPost('form-test/state-persist', array('title' => ''), t('Submit'), $options);
-      $this->assertText(t('!name field is required.', array('!name' => 'title')));
+      $this->assertText(t('!title field is required.', array('!title' => 'title')));
       // Submit the form again triggering no validation error.
       $this->drupalPost(NULL, array('title' => 'foo'), t('Submit'), $options);
       $this->assertText('State persisted.');
Index: modules/simpletest/tests/form_test.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/form_test.module,v
retrieving revision 1.51
diff -u -p -r1.51 form_test.module
--- modules/simpletest/tests/form_test.module	4 Oct 2010 18:00:46 -0000	1.51
+++ modules/simpletest/tests/form_test.module	7 Oct 2010 18:29:59 -0000
@@ -24,6 +24,12 @@ function form_test_menu() {
     'access arguments' => array('access content'),
     'type' => MENU_CALLBACK,
   );
+  $items['form-test/validate-required'] = array(
+    'title' => 'Form #required validation',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('form_test_validate_required_form'),
+    'access arguments' => array('access content'),
+  );
   $items['form-test/limit-validation-errors'] = array(
     'title' => 'Form validation with some error suppression',
     'page callback' => 'drupal_get_form',
@@ -304,6 +310,46 @@ function form_test_validate_form_validat
 }
 
 /**
+ * Form constructor for testing #required and custom form errors.
+ */
+function form_test_validate_required_form($form, &$form_state) {
+  $form['textfield'] = array(
+    '#type' => 'textfield',
+    '#title' => 'Textfield',
+    '#required' => TRUE,
+    '#required_message' => 'Yo, ur !title iz amptee!',
+  );
+  $form['textarea'] = array(
+    '#type' => 'textarea',
+    '#title' => 'Textarea',
+    '#required' => TRUE,
+    '#required_message' => 'Yo, ur !title iz amptee!',
+  );
+  $form['checkboxes'] = array(
+    '#type' => 'checkboxes',
+    '#title' => 'Checkboxes',
+    '#options' => array(
+      'option1' => 'Option1',
+      'option2' => 'Option2',
+    ),
+    '#required' => TRUE,
+    '#required_message' => 'Yo, ur !title iz amptee!',
+  );
+  $form['radios'] = array(
+    '#type' => 'radios',
+    '#title' => 'Radios',
+    '#options' => array(
+      'option1' => 'Option1',
+      'option2' => 'Option2',
+    ),
+    '#required' => TRUE,
+    '#required_message' => 'Yo, ur !title iz amptee!',
+  );
+  $form['submit'] = array('#type' => 'submit', '#value' => 'Submit');
+  return $form;
+}
+
+/**
  * Builds a simple form with a button triggering partial validation.
  */
 function form_test_limit_validation_errors_form($form, &$form_state) {
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.977
diff -u -p -r1.977 system.module
--- modules/system/system.module	7 Oct 2010 04:58:45 -0000	1.977
+++ modules/system/system.module	7 Oct 2010 18:30:00 -0000
@@ -371,6 +371,11 @@ function system_element_info() {
   );
   $types['radios'] = array(
     '#input' => TRUE,
+    // If no #default_value is specified and a user agent submits no value for
+    // #type 'radios', then _form_builder_handle_input_element() would assign an
+    // empty string as #value, which in turn would trigger an "illegal choice"
+    // error in _form_validate().
+    '#default_value' => NULL,
     '#process' => array('form_process_radios'),
     '#theme_wrappers' => array('radios'),
     '#pre_render' => array('form_pre_render_conditional_form_element'),
