diff --git a/core/includes/file.inc b/core/includes/file.inc index 6ac7b922269156420c13f86589a7565265188aa3..9e85ba9ee677746e5db4d957ffbacd9e58da86ab 100644 --- a/core/includes/file.inc +++ b/core/includes/file.inc @@ -569,6 +569,13 @@ function file_load($fid) { function file_save(stdClass $file) { $file->timestamp = REQUEST_TIME; $file->filesize = filesize($file->uri); + if (!isset($file->langcode)) { + // Default the file's language code to none, because files are language + // neutral more often than language dependent. Until we have better flexible + // settings. + // @todo See http://drupal.org/node/258785 and followups. + $file->langcode = LANGUAGE_NONE; + } // Load the stored entity, if any. if (!empty($file->fid) && !isset($file->original)) { diff --git a/core/includes/locale.inc b/core/includes/locale.inc index 8a381edbfe417db3235f2bf5266b2f59bf86dd49..43bac8d3093e88aec4b5cadb03e84ea706a6bb8a 100644 --- a/core/includes/locale.inc +++ b/core/includes/locale.inc @@ -215,7 +215,7 @@ function locale_language_from_user($languages) { global $user; if ($user->uid) { - return $user->language; + return $user->preferred_langcode; } // No language preference from the user. diff --git a/core/modules/entity/tests/entity_crud_hook_test.test b/core/modules/entity/tests/entity_crud_hook_test.test index 7c8e42ee8d344759b060dcef11352fb9b41c4175..97f44622dc1e8dee8be1423c1a7196ea15b85229 100644 --- a/core/modules/entity/tests/entity_crud_hook_test.test +++ b/core/modules/entity/tests/entity_crud_hook_test.test @@ -248,6 +248,7 @@ class EntityCrudHookTestCase extends DrupalWebTestCase { $vocabulary = (object) array( 'name' => 'Test vocabulary', 'machine_name' => 'test', + 'langcode' => LANGUAGE_NONE, 'description' => NULL, 'module' => 'entity_crud_hook_test', ); @@ -256,6 +257,7 @@ class EntityCrudHookTestCase extends DrupalWebTestCase { $term = (object) array( 'vid' => $vocabulary->vid, 'name' => 'Test term', + 'langcode' => LANGUAGE_NONE, 'description' => NULL, 'format' => 1, ); @@ -306,6 +308,7 @@ class EntityCrudHookTestCase extends DrupalWebTestCase { $vocabulary = (object) array( 'name' => 'Test vocabulary', 'machine_name' => 'test', + 'langcode' => LANGUAGE_NONE, 'description' => NULL, 'module' => 'entity_crud_hook_test', ); diff --git a/core/modules/field_ui/field_ui.test b/core/modules/field_ui/field_ui.test index c2e3a9820cc3aa49dc6166c6c4fd88c289d8768f..4f7369232ea1457cbfaa65e7c0a76cf64ff13c50 100644 --- a/core/modules/field_ui/field_ui.test +++ b/core/modules/field_ui/field_ui.test @@ -163,6 +163,7 @@ class FieldUIManageFieldsTestCase extends FieldUITestCase { $vocabulary = (object) array( 'name' => 'Tags', 'machine_name' => 'tags', + 'langcode' => LANGUAGE_NONE, ); taxonomy_vocabulary_save($vocabulary); diff --git a/core/modules/forum/forum.install b/core/modules/forum/forum.install index 2eebd7fba3ee2087bc06f8e152c39910222d1068..946f9c4b0511a5247b786da47df1397a95b85011 100644 --- a/core/modules/forum/forum.install +++ b/core/modules/forum/forum.install @@ -33,6 +33,7 @@ function forum_enable() { $edit = array( 'name' => t('Forums'), 'machine_name' => 'forums', + 'langcode' => language_default()->langcode, 'description' => t('Forum navigation vocabulary'), 'hierarchy' => 1, 'module' => 'forum', @@ -62,6 +63,7 @@ function forum_enable() { // Create a default forum so forum posts can be created. $edit = array( 'name' => t('General discussion'), + 'langcode' => language_default()->langcode, 'description' => '', 'parent' => array(0), 'vid' => $vocabulary->vid, diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module index 7cadf53ff2b6559736a31b4e9cc9a970082d6e1e..5c2cdbdf7eb8347b25e42db6198f9c7c3ba56383 100644 --- a/core/modules/locale/locale.module +++ b/core/modules/locale/locale.module @@ -236,16 +236,40 @@ function locale_language_selector_form($user) { '#title' => t('Language settings'), '#weight' => 1, ); - $form['locale']['language'] = array( + $form['locale']['preferred_langcode'] = array( '#type' => (count($names) <= 5 ? 'radios' : 'select'), '#title' => t('Language'), '#default_value' => $user_preferred_language->langcode, '#options' => $names, - '#description' => $mode ? t("This account's default language for e-mails, and preferred language for site presentation.") : t("This account's default language for e-mails."), + '#description' => $mode ? t("This account's preferred language for e-mails and site presentation.") : t("This account's preferred language for e-mails."), + ); + // User entities contain both a langcode property (for identifying the + // language of the entity data) and a preferred_langcode property (see above). + // Rather than provide a UI forcing the user to choose both separately, + // assume that the user profile data is in the user's preferred language. This + // element provides that synchronization. For use-cases where this + // synchronization is not desired, a module can alter or remove this element. + $form['locale']['langcode'] = array( + '#type' => 'value', + '#value_callback' => '_locale_language_selector_langcode_value', + // For the synchronization to work, this element must have a larger weight + // than the preferred_langcode element. Set a large weight here in case + // a module alters the weight of the other element. + '#weight' => 100, ); return $form; } +/** + * Sets the value of the user register and profile forms' langcode element. + * + * @see locale_language_selector_form() + */ +function _locale_language_selector_langcode_value($element, $input, &$form_state) { + $form_state['complete_form']['locale']['preferred_langcode']['#description'] .= ' ' . t("This is also assumed to be the primary language of this account's profile information."); + return $form_state['values']['preferred_langcode']; +} + /** * Implements hook_form_alter(). * diff --git a/core/modules/locale/locale.test b/core/modules/locale/locale.test index 208c5e6882d028eb7d46338e312f538146291283..7d1e55cd6e0a5358e3c60eed7b91d1bcaaf2d7cc 100644 --- a/core/modules/locale/locale.test +++ b/core/modules/locale/locale.test @@ -1644,13 +1644,13 @@ class LocaleUserLanguageFunctionalTest extends DrupalWebTestCase { $this->assertNoText($name_disabled, t('Disabled language not present on form.')); // Switch to our custom language. $edit = array( - 'language' => $langcode, + 'preferred_langcode' => $langcode, ); $this->drupalPost($path, $edit, t('Save')); // Ensure form was submitted successfully. $this->assertText(t('The changes have been saved.'), t('Changes were saved.')); // Check if language was changed. - $elements = $this->xpath('//input[@id=:id]', array(':id' => 'edit-language-' . $langcode)); + $elements = $this->xpath('//input[@id=:id]', array(':id' => 'edit-preferred-langcode-' . $langcode)); $this->assertTrue(isset($elements[0]) && !empty($elements[0]['checked']), t('Default language successfully updated.')); $this->drupalLogout(); @@ -1702,7 +1702,7 @@ class LocaleUserCreationTest extends DrupalWebTestCase { // Check if the language selector is available on admin/people/create and // set to the currently active language. $this->drupalGet($langcode . '/admin/people/create'); - $this->assertFieldChecked("edit-language-$langcode", t('Global language set in the language selector.')); + $this->assertFieldChecked("edit-preferred-langcode-$langcode", t('Global language set in the language selector.')); // Create a user with the admin/people/create form and check if the correct // language is set. @@ -1717,7 +1717,8 @@ class LocaleUserCreationTest extends DrupalWebTestCase { $this->drupalPost($langcode . '/admin/people/create', $edit, t('Create new account')); $user = user_load_by_name($username); - $this->assertEqual($user->language, $langcode, t('New user has correct language set.')); + $this->assertEqual($user->preferred_langcode, $langcode, t('New user has correct preferred language set.')); + $this->assertEqual($user->langcode, $langcode, t('New user has correct profile language set.')); // Register a new user and check if the language selector is hidden. $this->drupalLogout(); @@ -1734,7 +1735,8 @@ class LocaleUserCreationTest extends DrupalWebTestCase { $this->drupalPost($langcode . '/user/register', $edit, t('Create new account')); $user = user_load_by_name($username); - $this->assertEqual($user->language, $langcode, t('New user has correct language set.')); + $this->assertEqual($user->preferred_langcode, $langcode, t('New user has correct preferred language set.')); + $this->assertEqual($user->langcode, $langcode, t('New user has correct profile language set.')); // Test if the admin can use the language selector and if the // correct language is was saved. @@ -1742,7 +1744,7 @@ class LocaleUserCreationTest extends DrupalWebTestCase { $this->drupalLogin($admin_user); $this->drupalGet($user_edit); - $this->assertFieldChecked("edit-language-$langcode", t('Language selector is accessible and correct language is selected.')); + $this->assertFieldChecked("edit-preferred-langcode-$langcode", t('Language selector is accessible and correct language is selected.')); // Set pass_raw so we can login the new user. $user->pass_raw = $this->randomName(10); @@ -1755,7 +1757,7 @@ class LocaleUserCreationTest extends DrupalWebTestCase { $this->drupalLogin($user); $this->drupalGet($user_edit); - $this->assertFieldChecked("edit-language-$langcode", t('Language selector is accessible and correct language is selected.')); + $this->assertFieldChecked("edit-preferred-langcode-$langcode", t('Language selector is accessible and correct language is selected.')); } } @@ -2682,7 +2684,7 @@ class LocaleCommentLanguageFunctionalTest extends DrupalWebTestCase { // Change user language preference, this way interface language is always // French no matter what path prefix the URLs have. - $edit = array('language' => 'fr'); + $edit = array('preferred_langcode' => 'fr'); $this->drupalPost("user/{$admin_user->uid}/edit", $edit, t('Save')); } diff --git a/core/modules/openid/openid.module b/core/modules/openid/openid.module index d29d40578832ded4513aff161f2d3c351fc1bae0..a8e99a06e5b98d4ddeddecb43f4720bc968f9d70 100644 --- a/core/modules/openid/openid.module +++ b/core/modules/openid/openid.module @@ -268,8 +268,8 @@ function openid_form_user_register_form_alter(&$form, &$form_state) { // specific) strings. foreach (array_reverse($candidate_languages) as $candidate_language) { if (isset($enabled_languages[$candidate_language])) { - $form['locale']['language']['#type'] = 'hidden'; - $form['locale']['language']['#value'] = $candidate_language; + $form['locale']['preferred_langcode']['#type'] = 'hidden'; + $form['locale']['preferred_langcode']['#value'] = $candidate_language; } } } diff --git a/core/modules/openid/openid.test b/core/modules/openid/openid.test index 7f87b4b54514b3d6ca677c5dae59d61fae4f1a38..9d2b3352a6deaa51cd7f2e003301dc28d62d88b3 100644 --- a/core/modules/openid/openid.test +++ b/core/modules/openid/openid.test @@ -453,7 +453,7 @@ class OpenIDRegistrationTestCase extends OpenIDWebTestCase { $this->assertTrue($user, t('User was registered with right username.')); $this->assertEqual($user->mail, 'john@example.com', t('User was registered with right email address.')); $this->assertEqual($user->timezone, 'Europe/London', t('User was registered with right timezone.')); - $this->assertEqual($user->language, 'en', t('User was registered with right language.')); + $this->assertEqual($user->preferred_langcode, 'en', t('User was registered with right language.')); $this->assertFalse($user->data, t('No additional user info was saved.')); $this->submitLoginForm($identity); @@ -495,7 +495,7 @@ class OpenIDRegistrationTestCase extends OpenIDWebTestCase { $this->assertTrue($user, t('User was registered with right username.')); $this->assertEqual($user->mail, 'john@example.com', t('User was registered with right email address.')); $this->assertEqual($user->timezone, 'Europe/London', t('User was registered with right timezone.')); - $this->assertEqual($user->language, 'en', t('User was registered with right language.')); + $this->assertEqual($user->preferred_langcode, 'en', t('User was registered with right language.')); $this->assertFalse($user->data, t('No additional user info was saved.')); $this->drupalLogout(); @@ -540,7 +540,7 @@ class OpenIDRegistrationTestCase extends OpenIDWebTestCase { $user = user_load_by_name('john'); $this->assertTrue($user, t('User was registered with right username.')); - $this->assertFalse($user->language, t('No user language was saved.')); + $this->assertFalse($user->preferred_langcode, t('No user language was saved.')); $this->assertFalse($user->data, t('No additional user info was saved.')); // Follow the one-time login that was sent in the welcome e-mail. @@ -580,7 +580,7 @@ class OpenIDRegistrationTestCase extends OpenIDWebTestCase { $user = user_load_by_name('john'); $this->assertTrue($user, t('User was registered with right username.')); - $this->assertFalse($user->language, t('No user language was saved.')); + $this->assertFalse($user->preferred_langcode, t('No user language was saved.')); $this->assertFalse($user->data, t('No additional user info was saved.')); // Follow the one-time login that was sent in the welcome e-mail. @@ -625,7 +625,7 @@ class OpenIDRegistrationTestCase extends OpenIDWebTestCase { $this->assertTrue($user, t('User was registered with right username.')); $this->assertEqual($user->mail, 'john@example.com', t('User was registered with right email address.')); $this->assertEqual($user->timezone, 'Europe/London', t('User was registered with right timezone.')); - $this->assertEqual($user->language, 'en', t('User was registered with right language.')); + $this->assertEqual($user->preferred_langcode, 'en', t('User was registered with right language.')); } } diff --git a/core/modules/path/path.test b/core/modules/path/path.test index 7b6b2f2e9e55c55e70cab01af2d5ff3bd1b953db..3679553ad8f87d8128870554cc95755d87082e89 100644 --- a/core/modules/path/path.test +++ b/core/modules/path/path.test @@ -334,7 +334,7 @@ class PathLanguageTestCase extends DrupalWebTestCase { $this->drupalPost('admin/config/regional/language/detection', $edit, t('Save settings')); // Change user language preference. - $edit = array('language' => 'fr'); + $edit = array('preferred_langcode' => 'fr'); $this->drupalPost("user/{$this->web_user->uid}/edit", $edit, t('Save')); // Check that the English alias works. In this situation French is the diff --git a/core/modules/simpletest/tests/common.test b/core/modules/simpletest/tests/common.test index eeb5e497f6d2305756f35a89f0b33ac68b4ef44d..353ab1c7028ede2101cc4244b923919aaeb54181 100644 --- a/core/modules/simpletest/tests/common.test +++ b/core/modules/simpletest/tests/common.test @@ -2400,7 +2400,7 @@ class CommonFormatDateTestCase extends DrupalWebTestCase { // Create a test user to carry out the tests. $test_user = $this->drupalCreateUser(); $this->drupalLogin($test_user); - $edit = array('language' => self::LANGCODE, 'mail' => $test_user->mail, 'timezone' => 'America/Los_Angeles'); + $edit = array('preferred_langcode' => self::LANGCODE, 'mail' => $test_user->mail, 'timezone' => 'America/Los_Angeles'); $this->drupalPost('user/' . $test_user->uid . '/edit', $edit, t('Save')); // Disable session saving as we are about to modify the global $user. @@ -2409,7 +2409,7 @@ class CommonFormatDateTestCase extends DrupalWebTestCase { $real_user = $user; $user = user_load($test_user->uid, TRUE); $real_language = $language_interface->langcode; - $language_interface->langcode = $user->language; + $language_interface->langcode = $user->preferred_langcode; // Simulate a Drupal bootstrap with the logged-in user. date_default_timezone_set(drupal_get_user_timezone()); diff --git a/core/modules/simpletest/tests/file.test b/core/modules/simpletest/tests/file.test index a369a44ab6ef9367f44b0463e6a2d24e624f3fea..a186360f8ce0fafbd6e46c08039498a97cc0a3db 100644 --- a/core/modules/simpletest/tests/file.test +++ b/core/modules/simpletest/tests/file.test @@ -2011,11 +2011,12 @@ class FileSaveTest extends FileHookTestCase { $this->assertEqual($loaded_file->status, $file->status, t("Status was saved correctly.")); $this->assertEqual($saved_file->filesize, filesize($file->uri), t("File size was set correctly."), 'File'); $this->assertTrue($saved_file->timestamp > 1, t("File size was set correctly."), 'File'); - + $this->assertEqual($loaded_file->langcode, LANGUAGE_NONE, t("Langcode was defaulted correctly.")); // Resave the file, updating the existing record. file_test_reset(); $saved_file->status = 7; + $saved_file->langcode = 'en'; $resaved_file = file_save($saved_file); // Check that the correct hooks were called. @@ -2026,6 +2027,7 @@ class FileSaveTest extends FileHookTestCase { $loaded_file = db_query('SELECT * FROM {file_managed} f WHERE f.fid = :fid', array(':fid' => $saved_file->fid))->fetch(PDO::FETCH_OBJ); $this->assertNotNull($loaded_file, t("Record still exists in the database."), 'File'); $this->assertEqual($loaded_file->status, $saved_file->status, t("Status was saved correctly.")); + $this->assertEqual($loaded_file->langcode, 'en', t("Langcode was saved correctly.")); } } diff --git a/core/modules/simpletest/tests/upgrade/drupal-7.language.database.php b/core/modules/simpletest/tests/upgrade/drupal-7.language.database.php index c231f84e23ac50258e665bd9e5c305a3aba711e4..c1b9cc7801516b962538e45847636da973ca839a 100644 --- a/core/modules/simpletest/tests/upgrade/drupal-7.language.database.php +++ b/core/modules/simpletest/tests/upgrade/drupal-7.language.database.php @@ -878,3 +878,26 @@ 'comment_body_format' => 'filtered_html', )) ->execute(); + +// Add a managed file. +db_insert('file_managed')->fields(array( + 'fid', + 'uid', + 'filename', + 'uri', + 'filemime', + 'filesize', + 'status', + 'timestamp' +)) +->values(array( + 'fid' => '1', + 'uid' => '1', + 'filename' => 'foo.txt', + 'uri' => 'public://foo.txt', + 'filemime' => 'text/plain', + 'filesize' => 0, + 'status' => 1, + 'timestamp' => '1314997642', +)) +->execute(); diff --git a/core/modules/simpletest/tests/upgrade/upgrade.language.test b/core/modules/simpletest/tests/upgrade/upgrade.language.test index b132e1077a1bd1109fa41f4f249229c0953427e6..0dbbf2e99c6f1aaa830298b4a95852a45b23fc96 100644 --- a/core/modules/simpletest/tests/upgrade/upgrade.language.test +++ b/core/modules/simpletest/tests/upgrade/upgrade.language.test @@ -32,6 +32,7 @@ class LanguageUpgradePathTestCase extends UpgradePathTestCase { * Tests a successful upgrade. */ public function testLanguageUpgrade() { + db_update('users')->fields(array('language' => 'ca'))->condition('uid', '1')->execute(); $this->assertTrue($this->performUpgrade(), t('The upgrade was completed successfully.')); // Ensure Catalan was properly upgraded to be the new default language. @@ -77,5 +78,23 @@ class LanguageUpgradePathTestCase extends UpgradePathTestCase { $this->assertFieldByName('langcode'); $this->drupalGet('node/add/page'); $this->assertNoFieldByName('langcode'); + + // Check that the user language value was retained in both langcode and + // preferred_langcode. + $user = db_query('SELECT * FROM {users} WHERE uid = :uid', array(':uid' => 1))->fetchObject(); + $this->assertEqual($user->langcode, 'ca'); + $this->assertEqual($user->preferred_langcode, 'ca'); + + // A langcode property was added to vocabularies and terms. Check that + // existing vocabularies and terms got assigned the site default language. + $vocabulary = db_query('SELECT * FROM {taxonomy_vocabulary} WHERE vid = :vid', array(':vid' => 1))->fetchObject(); + $this->assertEqual($vocabulary->langcode, 'ca'); + $term = db_query('SELECT * FROM {taxonomy_term_data} WHERE tid = :tid', array(':tid' => 1))->fetchObject(); + $this->assertEqual($term->langcode, 'ca'); + + // A langcode property was added to files. Check that existing files got + // assigned LANGUAGE_NONE. + $file = db_query('SELECT * FROM {file_managed} WHERE fid = :fid', array(':fid' => 1))->fetchObject(); + $this->assertEqual($file->langcode, LANGUAGE_NONE); } } diff --git a/core/modules/system/system.install b/core/modules/system/system.install index 03116583761edc6ef55674c6b36da1e591fe3a8d..8ab35e102b2dbc7d2d0b162bd136c8547b430d35 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -838,6 +838,13 @@ function system_schema() { 'not null' => TRUE, 'default' => '', ), + 'langcode' => array( + 'description' => 'The {language}.langcode of this file.', + 'type' => 'varchar', + 'length' => 12, + 'not null' => TRUE, + 'default' => '', + ), 'filemime' => array( 'description' => "The file's MIME type.", 'type' => 'varchar', @@ -1719,6 +1726,40 @@ function system_update_8003() { )); } +/** + * Adds {file_managed}.langcode field. + * + * @see http://drupal.org/node/1454538 + */ +function system_update_8003() { + $langcode_field = array( + 'description' => 'The {language}.langcode of this file.', + 'type' => 'varchar', + 'length' => 12, + 'not null' => TRUE, + 'default' => '', + ); + + // If a Drupal 7 contrib module already added a langcode field to support + // internationalization, keep it, but standardize the specification. + // Otherwise, add the field. + if (db_field_exists('file_managed', 'langcode')) { + // According to the documentation of db_change_field(), indeces using the + // field should be dropped first; if the contrib module created any indeces, + // it is its responsibility to drop them in an update function that runs + // before this one, which it can enforce via hook_update_dependencies(). + db_change_field('file_managed', 'langcode', 'langcode', $langcode_field); + } + else { + // Files can be language-specific (e.g., a scanned document) or not (e.g., + // a photograph). For a site being updated, Drupal does not have a way to + // determine which existing files are language-specific and in what + // language. Our best guess is to set all of them to LANGUAGE_NONE. + $langcode_field['initial'] = LANGUAGE_NONE; + db_add_field('file_managed', 'langcode', $langcode_field); + } +} + /** * @} End of "defgroup updates-7.x-to-8.x" * The next series of updates should start at 9000. diff --git a/core/modules/taxonomy/taxonomy.admin.inc b/core/modules/taxonomy/taxonomy.admin.inc index bf8a52861e33285e72f7c7f4c7cd072f5db4627d..beb87f9d5a8153617e762178b2f4bb94663ec9c2 100644 --- a/core/modules/taxonomy/taxonomy.admin.inc +++ b/core/modules/taxonomy/taxonomy.admin.inc @@ -117,6 +117,10 @@ function taxonomy_form_vocabulary($form, &$form_state, $edit = array()) { 'description' => '', 'hierarchy' => TAXONOMY_HIERARCHY_DISABLED, 'weight' => 0, + // Default the new vocabulary to the site's default language. This is + // the most likely default value until we have better flexible settings. + // @todo See http://drupal.org/node/258785 and followups. + 'langcode' => language_default()->langcode, ); foreach ($defaults as $key => $value) { if (!isset($vocabulary->$key)) { @@ -659,6 +663,10 @@ function taxonomy_form_term($form, &$form_state, $edit = array(), $vocabulary = 'vocabulary_machine_name' => isset($vocabulary) ? $vocabulary->machine_name : NULL, 'tid' => NULL, 'weight' => 0, + // Default the new term to the site's default language. This is the most + // likely default value until we have better flexible settings. + // @todo See http://drupal.org/node/258785 and followups. + 'langcode' => language_default()->langcode, ); foreach ($defaults as $key => $value) { if (!isset($term->$key)) { diff --git a/core/modules/taxonomy/taxonomy.install b/core/modules/taxonomy/taxonomy.install index 3e07259f4118e2630b30a5a7ffaf8e3bc5e29a67..14b69568db648ef9d880957a2f3bd02c67613f39 100644 --- a/core/modules/taxonomy/taxonomy.install +++ b/core/modules/taxonomy/taxonomy.install @@ -39,6 +39,13 @@ function taxonomy_schema() { 'default' => 0, 'description' => 'The {taxonomy_vocabulary}.vid of the vocabulary to which the term is assigned.', ), + 'langcode' => array( + 'description' => 'The {language}.langcode of this term.', + 'type' => 'varchar', + 'length' => 12, + 'not null' => TRUE, + 'default' => '', + ), 'name' => array( 'type' => 'varchar', 'length' => 255, @@ -120,6 +127,13 @@ function taxonomy_schema() { 'not null' => TRUE, 'description' => 'Primary Key: Unique vocabulary ID.', ), + 'langcode' => array( + 'description' => 'The {language}.langcode of this vocabulary.', + 'type' => 'varchar', + 'length' => 12, + 'not null' => TRUE, + 'default' => '', + ), 'name' => array( 'type' => 'varchar', 'length' => 255, @@ -245,4 +259,44 @@ function taxonomy_field_schema($field) { */ function taxonomy_update_8000() { db_drop_field('taxonomy_vocabulary', 'module'); -} \ No newline at end of file +} + +/** + * Adds langcode field to {taxonomy_term_data} and {taxonomy_vocabulary}. + * + * @see http://drupal.org/node/1454538 + */ +function taxonomy_update_8001() { + $descriptions = array( + 'taxonomy_term_data' => 'The {language}.langcode of this term.', + 'taxonomy_vocabulary' => 'The {language}.langcode of this vocabulary.', + ); + foreach ($descriptions as $table => $description) { + $langcode_field = array( + 'description' => $description, + 'type' => 'varchar', + 'length' => 12, + 'not null' => TRUE, + 'default' => '', + ); + + // If a Drupal 7 contrib module already added a langcode field to support + // internationalization, keep it, but standardize the specification. + // Otherwise, add the field. + if (db_field_exists($table, 'langcode')) { + // According to the documentation of db_change_field(), indeces using the + // field should be dropped first; if the contrib module created any + // indeces, it is its responsibility to drop them in an update function + // that runs before this one, which it can enforce via + // hook_update_dependencies(). + db_change_field($table, 'langcode', 'langcode', $langcode_field); + } + else { + // When updating from a site that did not already have taxonomy + // internationalization, initialize all existing vocabularies and terms as + // being in the site's default language. + $langcode_field['initial'] = language_default()->langcode; + db_add_field($table, 'langcode', $langcode_field); + } + } +} diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module index eac6bed3fb250e621a5870945319aca25881aabb..23a0a6bc5d37c1a6aed5ab9961d2445d79ba40fe 100644 --- a/core/modules/taxonomy/taxonomy.module +++ b/core/modules/taxonomy/taxonomy.module @@ -434,6 +434,7 @@ function taxonomy_admin_vocabulary_title_callback($vocabulary) { * - vid: The ID of the vocabulary. * - name: The human-readable name of the vocabulary. * - machine_name: The machine name of the vocabulary. + * - langcode: The language code of the vocabulary. * - description: (optional) The vocabulary's description. * - hierarchy: The hierarchy level of the vocabulary. * - module: (optional) The module altering the vocabulary. @@ -638,6 +639,7 @@ function taxonomy_check_vocabulary_hierarchy($vocabulary, $changed_term) { * The taxonomy term object with the following properties: * - vid: The ID of the vocabulary the term is assigned to. * - name: The name of the term. + * - langcode: The language code of the term. * - tid: (optional) The unique ID for the term being saved. If $term->tid is * empty or omitted, a new term will be inserted. * - description: (optional) The term's description. @@ -1829,6 +1831,7 @@ function taxonomy_field_presave($entity_type, $entity, $field, $instance, $langc if ($item['tid'] == 'autocreate') { $term = (object) $item; unset($term->tid); + $term->langcode = $langcode; taxonomy_term_save($term); $items[$delta]['tid'] = $term->tid; } diff --git a/core/modules/taxonomy/taxonomy.test b/core/modules/taxonomy/taxonomy.test index b262b86ed18223b3c92216211372d2704b2e6e38..845855d14ac6266befefe9d5aa6d278543b2d082 100644 --- a/core/modules/taxonomy/taxonomy.test +++ b/core/modules/taxonomy/taxonomy.test @@ -19,6 +19,7 @@ class TaxonomyWebTestCase extends DrupalWebTestCase { $vocabulary->name = $this->randomName(); $vocabulary->description = $this->randomName(); $vocabulary->machine_name = drupal_strtolower($this->randomName()); + $vocabulary->langcode = LANGUAGE_NONE; $vocabulary->help = ''; $vocabulary->nodes = array('article' => 'article'); $vocabulary->weight = mt_rand(0, 10); @@ -36,6 +37,7 @@ class TaxonomyWebTestCase extends DrupalWebTestCase { // Use the first available text format. $term->format = db_query_range('SELECT format FROM {filter_format}', 0, 1)->fetchField(); $term->vid = $vocabulary->vid; + $term->langcode = LANGUAGE_NONE; taxonomy_term_save($term); return $term; } diff --git a/core/modules/user/user.install b/core/modules/user/user.install index f7175c3d22f05364fc9a0bfd9759469e11292e14..9b70e09fdca64b3655a17570f9063e6ca6ff244e 100644 --- a/core/modules/user/user.install +++ b/core/modules/user/user.install @@ -137,6 +137,13 @@ function user_schema() { 'default' => '', 'description' => 'Unique user name.', ), + 'langcode' => array( + 'type' => 'varchar', + 'length' => 12, + 'not null' => TRUE, + 'default' => '', + 'description' => "The {language}.langcode of the user's profile.", + ), 'pass' => array( 'type' => 'varchar', 'length' => 128, @@ -202,12 +209,12 @@ function user_schema() { 'not null' => FALSE, 'description' => "User's time zone.", ), - 'language' => array( + 'preferred_langcode' => array( 'type' => 'varchar', 'length' => 12, 'not null' => TRUE, 'default' => '', - 'description' => "User's default language.", + 'description' => 'The {language}.langcode that the user prefers for receiving emails and viewing the site.', ), 'picture' => array( 'type' => 'int', @@ -353,6 +360,41 @@ function user_update_8000() { } } +/** + * Splits {users}.language field to langcode and preferred_langcode. + * + * @see http://drupal.org/node/1454538 + */ +function user_update_8001() { + // The former language field is the language preference of the user. Rename + // this to preferred_langcode in order to distinguish it from the langcode + // field common to all entity types, used for identifying the language of the + // entity itself. + $preferred_langcode_field = array( + 'type' => 'varchar', + 'length' => 12, + 'not null' => TRUE, + 'default' => '', + 'description' => 'The {language}.langcode that the user prefers for receiving emails and viewing the site.', + ); + db_change_field('users', 'language', 'preferred_langcode', $preferred_langcode_field); + + // Add the langcode field. + $langcode_field = array( + 'type' => 'varchar', + 'length' => 12, + 'not null' => TRUE, + 'default' => '', + 'description' => "The {language}.langcode of the user's profile.", + ); + db_add_field('users', 'langcode', $langcode_field); + + // Since distinguishing the language of the user entity from the user's + // preferred language is a new feature in Drupal 8, assume that for updated + // sites, existing user entities are in the user's preferred language. + db_update('users')->expression('langcode', 'preferred_langcode')->execute(); +} + /** * @} End of "addtogroup updates-7.x-to-8.x" */ diff --git a/core/modules/user/user.module b/core/modules/user/user.module index 7fb442f15d070e64da0f86b758ec19eeb541fad4..00c91ac31e0dbeb205917bb4755d658422c9f4ff 100644 --- a/core/modules/user/user.module +++ b/core/modules/user/user.module @@ -422,6 +422,10 @@ function user_save($account, $edit = array()) { foreach ($edit as $key => $value) { $account->$key = $value; } + // Default the user entity language to the user's preferred language. + if (!isset($account->langcode) && isset($account->preferred_langcode)) { + $account->langcode = $account->preferred_langcode; + } field_attach_presave('user', $account); module_invoke_all('entity_presave', $account, 'user'); @@ -3429,8 +3433,8 @@ function theme_user_signature($variables) { */ function user_preferred_language($account, $default = NULL) { $language_list = language_list(); - if (!empty($account->language) && isset($language_list[$account->language])) { - return $language_list[$account->language]; + if (!empty($account->preferred_langcode) && isset($language_list[$account->preferred_langcode])) { + return $language_list[$account->preferred_langcode]; } else { return $default ? $default : language_default(); diff --git a/core/modules/user/user.test b/core/modules/user/user.test index 5b97e8b3e1ae87f8c83074d1fad29dd0bafbfb5d..a2f7ab10b2489c0e20a2db0ec05d2db11fb740f7 100644 --- a/core/modules/user/user.test +++ b/core/modules/user/user.test @@ -166,7 +166,8 @@ class UserRegistrationTestCase extends DrupalWebTestCase { $this->assertTrue(($new_user->created > REQUEST_TIME - 20 ), t('Correct creation time.')); $this->assertEqual($new_user->status, variable_get('user_register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL) == USER_REGISTER_VISITORS ? 1 : 0, t('Correct status field.')); $this->assertEqual($new_user->timezone, variable_get('date_default_timezone'), t('Correct time zone field.')); - $this->assertEqual($new_user->language, '', t('Correct language field.')); + $this->assertEqual($new_user->langcode, '', t('Correct language field.')); + $this->assertEqual($new_user->preferred_langcode, '', t('Correct preferred language field.')); $this->assertEqual($new_user->picture, '', t('Correct picture field.')); $this->assertEqual($new_user->init, $mail, t('Correct init field.')); } diff --git a/core/modules/user/user.tokens.inc b/core/modules/user/user.tokens.inc index 76ec4a2c873afd11a568fd16e62a1b3c25f28416..833e7636a173a6da44210756b7c9bd22ff012652 100644 --- a/core/modules/user/user.tokens.inc +++ b/core/modules/user/user.tokens.inc @@ -65,10 +65,10 @@ function user_tokens($type, $tokens, array $data = array(), array $options = arr $url_options = array('absolute' => TRUE); if (isset($options['language'])) { $url_options['language'] = $options['language']; - $language_code = $options['language']->langcode; + $langcode = $options['language']->langcode; } else { - $language_code = NULL; + $langcode = NULL; } $sanitize = !empty($options['sanitize']); @@ -103,12 +103,12 @@ function user_tokens($type, $tokens, array $data = array(), array $options = arr // These tokens are default variations on the chained tokens handled below. case 'last-login': - $replacements[$original] = !empty($account->login) ? format_date($account->login, 'medium', '', NULL, $language_code) : t('never'); + $replacements[$original] = !empty($account->login) ? format_date($account->login, 'medium', '', NULL, $langcode) : t('never'); break; case 'created': // In the case of user_presave the created date may not yet be set. - $replacements[$original] = !empty($account->created) ? format_date($account->created, 'medium', '', NULL, $language_code) : t('not yet created'); + $replacements[$original] = !empty($account->created) ? format_date($account->created, 'medium', '', NULL, $langcode) : t('not yet created'); break; } } diff --git a/profiles/standard/standard.install b/profiles/standard/standard.install index 1c8636d7454ed2606f613724f254235f6ba9b65c..d62b8d3003133c335e8e96859a62ccb84b6e0461 100644 --- a/profiles/standard/standard.install +++ b/profiles/standard/standard.install @@ -289,6 +289,7 @@ function standard_install() { 'name' => st('Tags'), 'description' => $description, 'machine_name' => 'tags', + 'langcode' => language_default()->langcode, 'help' => $help, );