diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageUILanguageNegotiationTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageUILanguageNegotiationTest.php index bc50f02..02d5139 100644 --- a/core/modules/language/lib/Drupal/language/Tests/LanguageUILanguageNegotiationTest.php +++ b/core/modules/language/lib/Drupal/language/Tests/LanguageUILanguageNegotiationTest.php @@ -103,15 +103,29 @@ class LanguageUILanguageNegotiationTest extends WebTestBase { $language_browser_fallback_string = "In $langcode_browser_fallback In $langcode_browser_fallback In $langcode_browser_fallback"; $language_string = "In $langcode In $langcode In $langcode"; // Do a translate search of our target string. - $edit = array( 'string' => $default_string); - $this->drupalPost('admin/config/regional/translate/translate', $edit, t('Filter')); - // Should find the string and now click edit to post translated string. - $this->clickLink('edit'); + $search = array( + 'string' => $default_string, + 'langcode' => $langcode_browser_fallback, + ); + $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); + $textarea = current($this->xpath('//textarea')); + $lid = (string) $textarea[0]['name']; + $edit = array( + $lid => $language_browser_fallback_string, + ); + $this->drupalPost('admin/config/regional/translate/translate', $edit, t('Save translations')); + + $search = array( + 'string' => $default_string, + 'langcode' => $langcode, + ); + $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); + $textarea = current($this->xpath('//textarea')); + $lid = (string) $textarea[0]['name']; $edit = array( - "translations[$langcode_browser_fallback][0]" => $language_browser_fallback_string, - "translations[$langcode][0]" => $language_string, + $lid => $language_string, ); - $this->drupalPost(NULL, $edit, t('Save translations')); + $this->drupalPost('admin/config/regional/translate/translate', $edit, t('Save translations')); // Configure URL language rewrite. variable_set('language_negotiation_url_type', LANGUAGE_TYPE_INTERFACE); diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleImportFunctionalTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleImportFunctionalTest.php index 3beee64..6fe77c2 100644 --- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleImportFunctionalTest.php +++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleImportFunctionalTest.php @@ -91,7 +91,7 @@ class LocaleImportFunctionalTest extends WebTestBase { // Ensure string wasn't overwritten. $search = array( 'string' => 'Montag', - 'language' => 'fr', + 'langcode' => 'fr', 'translation' => 'translated', ); $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); @@ -113,7 +113,7 @@ class LocaleImportFunctionalTest extends WebTestBase { // Ensure string was overwritten. $search = array( 'string' => 'Montag', - 'language' => 'fr', + 'langcode' => 'fr', 'translation' => 'translated', ); $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); @@ -149,7 +149,7 @@ class LocaleImportFunctionalTest extends WebTestBase { // Ensure string wasn't overwritten. $search = array( 'string' => 'januari', - 'language' => 'fr', + 'langcode' => 'fr', 'translation' => 'translated', ); $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); @@ -168,7 +168,7 @@ class LocaleImportFunctionalTest extends WebTestBase { // Ensure string was overwritten. $search = array( 'string' => 'januari', - 'language' => 'fr', + 'langcode' => 'fr', 'translation' => 'translated', ); $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); @@ -201,7 +201,7 @@ class LocaleImportFunctionalTest extends WebTestBase { // Ensure strings were successfully imported. $search = array( 'string' => 'lundi', - 'language' => $langcode, + 'langcode' => $langcode, 'translation' => 'translated', ); $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); @@ -241,20 +241,15 @@ class LocaleImportFunctionalTest extends WebTestBase { 'overwrite_options[not_customized]' => TRUE, )); $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 0, '%update' => 0, '%delete' => 1)), t('The translation file was successfully imported.')); - // This is the language indicator on the translation search screen for - // untranslated strings. - $language_indicator = "$langcode "; + $str = "Operations"; $search = array( 'string' => $str, - 'language' => 'all', - 'translation' => 'all', + 'langcode' => $langcode, + 'translation' => 'untranslated', ); $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); - // assertText() seems to remove the input field where $str always could be - // found, so this is not a false assert. - $this->assertText($str, t('Search found the string.')); - $this->assertRaw($language_indicator, t('String is untranslated again.')); + $this->assertText($str, t('Search found the string as untranslated.')); } /** diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocalePluralFormatTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocalePluralFormatTest.php index c0cf178..725a917 100644 --- a/core/modules/locale/lib/Drupal/locale/Tests/LocalePluralFormatTest.php +++ b/core/modules/locale/lib/Drupal/locale/Tests/LocalePluralFormatTest.php @@ -160,32 +160,41 @@ class LocalePluralFormatTest extends WebTestBase { // Check if the source appears on the translation page. $this->drupalGet('admin/config/regional/translate'); - $this->assertText("1 hour, @count hours"); + $this->assertText("1 hour"); + $this->assertText("@count hours"); // Look up editing page for this plural string and check fields. - $lid = db_query("SELECT lid FROM {locales_source} WHERE source = :source AND context = ''", array(':source' => "1 hour" . LOCALE_PLURAL_DELIMITER . "@count hours"))->fetchField(); - $path = 'admin/config/regional/translate/edit/' . $lid; + $path = 'admin/config/regional/translate/'; $this->drupalGet($path); - // Labels for plural editing elements. - $this->assertFieldByXPath('//label[@for="edit-translations-hr-0"]', 'Singular form '); - $this->assertFieldByXPath('//label[@for="edit-translations-hr-1"]', 'First plural form '); - $this->assertFieldByXPath('//label[@for="edit-translations-hr-2"]', '2. plural form '); - $this->assertFieldByXPath('//label[@for="edit-translations-fr-0"]', 'Singular form '); - $this->assertFieldByXPath('//label[@for="edit-translations-fr-1"]', 'Plural form '); - - // Plural values for both languages. - $this->assertFieldById('edit-translations-hr-0', '@count sat'); - $this->assertFieldById('edit-translations-hr-1', '@count sata'); - $this->assertFieldById('edit-translations-hr-2', '@count sati'); - $this->assertNoFieldById('edit-translations-hr-3'); - $this->assertFieldById('edit-translations-fr-0', '1 heure'); - $this->assertFieldById('edit-translations-fr-1', '@count heures'); - $this->assertNoFieldById('edit-translations-fr-2'); - - // Edit some translations and see if that took effect. + // Labels for plural editing elements. + $this->assertText('Singular form'); + $this->assertText('First plural form'); + $this->assertText('2. plural form'); + $this->assertNoText('3. plural form'); + + // Plural values for langcode hr. + $this->assertText('@count sat'); + $this->assertText('@count sata'); + $this->assertText('@count sati'); + + // Edit langcode hr translations and see if that took effect. $edit = array( - 'translations[fr][0]' => '1 heure edited', - 'translations[hr][1]' => '@count sata edited', + 'strings[10][translations][1]' => '@count sata edited', + ); + $this->drupalPost($path, $edit, t('Save translations')); + + $search = array( + 'langcode' => 'fr', + ); + $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); + // Plural values for the langcode fr. + $this->assertText('1 heure'); + $this->assertText('@count heures'); + $this->assertNoText('2. plural form'); + + // Edit langcode fr translations and see if that took effect. + $edit = array( + 'strings[10][translations][0]' => '1 heure edited', ); $this->drupalPost($path, $edit, t('Save translations')); @@ -194,21 +203,36 @@ class LocalePluralFormatTest extends WebTestBase { // not save our source string for performance optimization if we do not ask // specifically for a language. format_plural(1, '1 day', '@count days', array(), array('langcode' => 'fr')); - // Look up editing page for this plural string and check fields. $lid = db_query("SELECT lid FROM {locales_source} WHERE source = :source AND context = ''", array(':source' => "1 day" . LOCALE_PLURAL_DELIMITER . "@count days"))->fetchField(); - $path = 'admin/config/regional/translate/edit/' . $lid; + // Look up editing page for this plural string and check fields. + $search = array( + 'string' => '1 day', + 'langcode' => 'fr', + ); + $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); - // Save complete translations for the string in both languages. + // Save complete translations for the string in langcode fr. $edit = array( - 'translations[fr][0]' => '1 jour', - 'translations[fr][1]' => '@count jours', - 'translations[hr][0]' => '@count dan', - 'translations[hr][1]' => '@count dana', - 'translations[hr][2]' => '@count dana', + "strings[$lid][translations][0]" => '1 jour', + "strings[$lid][translations][1]" => '@count jours', ); $this->drupalPost($path, $edit, t('Save translations')); - // Get the French translations. + // Save complete translations for the string in langcode hr. + $search = array( + 'string' => '1 day', + 'langcode' => 'hr', + ); + $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); + + $edit = array( + "strings[$lid][translations][0]" => '@count dan', + "strings[$lid][translations][1]" => '@count dana', + "strings[$lid][translations][2]" => '@count dana', + ); + $this->drupalPost($path, $edit, t('Save translations')); + + // Get the French translations. $this->drupalPost('admin/config/regional/translate/export', array( 'langcode' => 'fr', ), t('Export')); @@ -225,7 +249,6 @@ class LocalePluralFormatTest extends WebTestBase { $this->assertRaw("msgid \"1 day\"\nmsgid_plural \"@count days\"\nmsgstr[0] \"@count dan\"\nmsgstr[1] \"@count dana\"\nmsgstr[2] \"@count dana\"", t('Added Croatian plural translations exported properly.')); } - /** * Imports a standalone .po file in a given language. * diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationTest.php index 7db6aef..212f32e 100644 --- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationTest.php +++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationTest.php @@ -67,21 +67,19 @@ class LocaleTranslationTest extends WebTestBase { $this->drupalLogin($translate_user); $search = array( 'string' => $name, - 'language' => 'all', - 'translation' => 'all', + 'langcode' => $langcode, + 'translation' => 'untranslated', ); $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); - // assertText() seems to remove the input field where $name always could be - // found, so this is not a false assert. See how assertNoText succeeds - // later. - $this->assertText($name, t('Search found the name.')); - $this->assertRaw($language_indicator, t('Name is untranslated.')); + $this->assertText($name, t('Search found the string as untranslated.')); + // Assume this is the only result, given the random name. - $this->clickLink(t('edit')); // We save the lid from the path. - $matches = array(); - preg_match('!admin/config/regional/translate/edit/(\d+)!', $this->getUrl(), $matches); - $lid = $matches[1]; + $textarea = current($this->xpath('//textarea')); + $lid = (string) $textarea[0]['name']; + $edit = array( + $lid => $this->randomName(), + ); // No t() here, it's surely not translated yet. $this->assertText($name, t('name found on edit screen.')); $this->assertNoText('English', t('No way to translate the string to English.')); @@ -91,24 +89,46 @@ class LocaleTranslationTest extends WebTestBase { $this->drupalLogout(); $this->drupalLogin($translate_user); $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); - // assertText() seems to remove the input field where $name always could be - // found, so this is not a false assert. See how assertNoText succeeds - // later. - $this->assertText($name, t('Search found the name.')); - $this->assertRaw($language_indicator, t('Name is untranslated.')); + $this->assertText($name, t('Search found the string as untranslated.')); + // Assume this is the only result, given the random name. - $this->clickLink(t('edit')); - $string_edit_url = $this->getUrl(); + $textarea = current($this->xpath('//textarea')); + $lid = (string) $textarea[0]['name']; $edit = array( - "translations[$langcode][0]" => $translation, - 'translations[en][0]' => $translation_to_en, + $lid => $translation, ); - $this->drupalPost(NULL, $edit, t('Save translations')); - $this->assertText(t('The string has been saved.'), t('The string has been saved.')); + $this->drupalPost('admin/config/regional/translate/translate', $edit, t('Save translations')); + $this->assertText(t('The strings have been saved.'), t('The strings have been saved.')); $this->assertEqual($this->getUrl(), url('admin/config/regional/translate/translate', array('absolute' => TRUE)), t('Correct page redirection.')); - $this->drupalGet($string_edit_url); + $search = array( + 'string' => $name, + 'langcode' => $langcode, + 'translation' => 'translated', + ); + $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); $this->assertRaw($translation, t('Non-English translation properly saved.')); + + + $search = array( + 'string' => $name, + 'langcode' => 'en', + 'translation' => 'untranslated', + ); + $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); + $textarea = current($this->xpath('//textarea')); + $lid = (string) $textarea[0]['name']; + $edit = array( + $lid => $translation_to_en, + ); + $this->drupalPost('admin/config/regional/translate/translate', $edit, t('Save translations')); + $search = array( + 'string' => $name, + 'langcode' => 'en', + 'translation' => 'translated', + ); + $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); $this->assertRaw($translation_to_en, t('English translation properly saved.')); + $this->assertTrue($name != $translation && t($name, array(), array('langcode' => $langcode)) == $translation, t('t() works for non-English.')); // Refresh the locale() cache to get fresh data from t() below. We are in // the same HTTP request and therefore t() is not refreshed by saving the @@ -117,16 +137,15 @@ class LocaleTranslationTest extends WebTestBase { // Now we should get the proper fresh translation from t(). $this->assertTrue($name != $translation_to_en && t($name, array(), array('langcode' => 'en')) == $translation_to_en, t('t() works for English.')); $this->assertTrue(t($name, array(), array('langcode' => LANGUAGE_SYSTEM)) == $name, t('t() works for LANGUAGE_SYSTEM.')); + + $search = array( + 'string' => $name, + 'langcode' => 'en', + 'translation' => 'untranslated', + ); $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); - // The indicator should not be here. - $this->assertNoRaw($language_indicator, t('String is translated.')); - - // Try to edit a non-existent string and ensure we're redirected correctly. - // Assuming we don't have 999,999 strings already. - $random_lid = 999999; - $this->drupalGet('admin/config/regional/translate/edit/' . $random_lid); - $this->assertText(t('String not found'), t('String not found.')); - $this->assertEqual($this->getUrl(), url('admin/config/regional/translate/translate', array('absolute' => TRUE)), t('Correct page redirection.')); + $this->assertText(t('No strings available.'), t('String is translated.')); + $this->drupalLogout(); // Delete the language. @@ -147,26 +166,26 @@ class LocaleTranslationTest extends WebTestBase { $this->drupalLogin($translate_user); $search = array( 'string' => $name, - 'language' => 'all', - 'translation' => 'all', + 'langcode' => 'en', + 'translation' => 'translated', ); $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); // Assume this is the only result, given the random name. - $this->clickLink(t('delete')); - $this->assertText(t('Are you sure you want to delete the string'), t('"delete" link is correct.')); - // Delete the string. - $path = 'admin/config/regional/translate/delete/' . $lid; - $this->drupalGet($path); - // First test the 'cancel' link. - $this->clickLink(t('Cancel')); - $this->assertEqual($this->getUrl(), url('admin/config/regional/translate/translate', array('absolute' => TRUE)), t('Correct page redirection.')); - $this->assertRaw($name, t('The string was not deleted.')); - // Delete the name string. - $this->drupalPost('admin/config/regional/translate/delete/' . $lid, array(), t('Delete')); - $this->assertText(t('The string has been removed.'), t('The string has been removed message.')); - $this->assertEqual($this->getUrl(), url('admin/config/regional/translate/translate', array('absolute' => TRUE)), t('Correct page redirection.')); + $textarea = current($this->xpath('//textarea')); + $lid = (string) $textarea[0]['name']; + $edit = array( + $lid => '', + ); + $this->drupalPost('admin/config/regional/translate/translate', $edit, t('Save translations')); + $this->assertRaw($name, t('The strings have been saved.')); + $this->drupalLogin($translate_user); + $search = array( + 'string' => $name, + 'langcode' => 'en', + 'translation' => 'untranslated', + ); $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); - $this->assertNoText($name, t('Search now can not find the name.')); + $this->assertNoText(t('No strings available.'), t('The translation has been removed')); } /* @@ -192,16 +211,29 @@ class LocaleTranslationTest extends WebTestBase { drupal_static_reset('language_list'); // Build the JavaScript translation file. - $this->drupalGet('admin/config/regional/translate/translate'); - // Retrieve the id of the first string available in the {locales_source} - // table and translate it. - $query = db_select('locales_source', 'l'); - $query->addExpression('min(l.lid)', 'lid'); - $result = $query->condition('l.location', '%.js%', 'LIKE')->execute(); - $url = 'admin/config/regional/translate/edit/' . $result->fetchObject()->lid; - $edit = array('translations['. $langcode .'][0]' => $this->randomName()); - $this->drupalPost($url, $edit, t('Save translations')); + // Retrieve the source string of the first string available in the + // {locales_source} table and translate it. + $source = db_select('locales_source', 'l') + ->fields('l', array('source')) + ->condition('l.location', '%.js%', 'LIKE') + ->range(0, 1) + ->execute() + ->fetchField(); + + $search = array( + 'string' => $source, + 'langcode' => $langcode, + 'translation' => 'all', + ); + $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); + + $textarea = current($this->xpath('//textarea')); + $lid = (string) $textarea[0]['name']; + $edit = array( + $lid => $this->randomName(), + ); + $this->drupalPost('admin/config/regional/translate/translate', $edit, t('Save translations')); // Trigger JavaScript translation parsing and building. _locale_rebuild_js($langcode); @@ -230,9 +262,7 @@ class LocaleTranslationTest extends WebTestBase { $langcode = 'xx'; // The English name for the language. This will be translated. $name = $this->randomName(16); - // This is the language indicator on the translation search screen for - // untranslated strings. - $language_indicator = "$langcode "; + // These will be the invalid translations of $name. $key = $this->randomName(16); $bad_translations[$key] = "" . $key; @@ -256,19 +286,19 @@ class LocaleTranslationTest extends WebTestBase { // Reset locale cache. $search = array( 'string' => $name, - 'language' => 'all', + 'langcode' => $langcode, 'translation' => 'all', ); $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); // Find the edit path. - $content = $this->drupalGetContent(); - $this->assertTrue(preg_match('@(admin/config/regional/translate/edit/[0-9]+)@', $content, $matches), t('Found the edit path.')); - $path = $matches[0]; + + $textarea = current($this->xpath('//textarea')); + $lid = (string) $textarea[0]['name']; foreach ($bad_translations as $key => $translation) { $edit = array( - "translations[$langcode][0]" => $translation, + $lid => $translation, ); - $this->drupalPost($path, $edit, t('Save translations')); + $this->drupalPost('admin/config/regional/translate/translate', $edit, t('Save translations')); // Check for a form error on the textarea. $form_class = $this->xpath('//form[@id="locale-translate-edit-form"]//textarea/@class'); $this->assertNotIdentical(FALSE, strpos($form_class[0], 'error'), t('The string was rejected as unsafe.')); @@ -306,6 +336,15 @@ class LocaleTranslationTest extends WebTestBase { 'direction' => '0', ); $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language')); + + $edit = array( + 'predefined_langcode' => 'custom', + 'langcode' => 'yy', + 'name' => $this->randomName(16), + 'direction' => '0', + ); + $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language')); + // Add string. t($name, array(), array('langcode' => $langcode)); // Reset locale cache. @@ -316,7 +355,7 @@ class LocaleTranslationTest extends WebTestBase { $this->drupalLogin($translate_user); $search = array( 'string' => $name, - 'language' => 'all', + 'langcode' => $langcode, 'translation' => 'all', ); $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); @@ -329,7 +368,7 @@ class LocaleTranslationTest extends WebTestBase { // translated strings'. $search = array( 'string' => $name, - 'language' => 'all', + 'langcode' => $langcode, 'translation' => 'translated', ); $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); @@ -339,7 +378,7 @@ class LocaleTranslationTest extends WebTestBase { // strings'. $search = array( 'string' => $name, - 'language' => 'all', + 'langcode' => $langcode, 'translation' => 'untranslated', ); $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); @@ -347,21 +386,19 @@ class LocaleTranslationTest extends WebTestBase { // Add translation. // Assume this is the only result, given the random name. - $this->clickLink(t('edit')); // We save the lid from the path. - $matches = array(); - preg_match('!admin/config/regional/translate/edit/(\d)+!', $this->getUrl(), $matches); - $lid = $matches[1]; + $textarea = current($this->xpath('//textarea')); + $lid = (string) $textarea[0]['name']; $edit = array( - "translations[$langcode][0]" => $translation, + $lid => $translation, ); - $this->drupalPost(NULL, $edit, t('Save translations')); + $this->drupalPost('admin/config/regional/translate/translate', $edit, t('Save translations')); // Ensure translated string does appear if searching on 'only // translated strings'. $search = array( 'string' => $translation, - 'language' => 'all', + 'langcode' => $langcode, 'translation' => 'translated', ); $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); @@ -371,7 +408,7 @@ class LocaleTranslationTest extends WebTestBase { // untranslated strings'. $search = array( 'string' => $name, - 'language' => 'all', + 'langcode' => $langcode, 'translation' => 'untranslated', ); $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); @@ -381,7 +418,7 @@ class LocaleTranslationTest extends WebTestBase { // untranslated strings'. $search = array( 'string' => $translation, - 'language' => 'all', + 'langcode' => $langcode, 'translation' => 'untranslated', ); $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); @@ -390,7 +427,7 @@ class LocaleTranslationTest extends WebTestBase { // Ensure translated string does appear if searching on the custom language. $search = array( 'string' => $translation, - 'language' => $langcode, + 'langcode' => $langcode, 'translation' => 'all', ); $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); @@ -399,7 +436,7 @@ class LocaleTranslationTest extends WebTestBase { // Ensure translated string doesn't appear if searching in System (English). $search = array( 'string' => $translation, - 'language' => LANGUAGE_SYSTEM, + 'langcode' => 'yy', 'translation' => 'all', ); $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); @@ -409,7 +446,7 @@ class LocaleTranslationTest extends WebTestBase { $unavailable_string = $this->randomName(16); $search = array( 'string' => $unavailable_string, - 'language' => 'all', + 'langcode' => $langcode, 'translation' => 'all', ); $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleUninstallTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleUninstallTest.php index 72868e9..71aff68 100644 --- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleUninstallTest.php +++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleUninstallTest.php @@ -61,11 +61,13 @@ class LocaleUninstallTest extends WebTestBase { $user = $this->drupalCreateUser(array('translate interface', 'access administration pages')); $this->drupalLogin($user); $this->drupalGet('admin/config/regional/translate/translate'); - $string = db_query('SELECT min(lid) AS lid FROM {locales_source} WHERE location LIKE :location', array( + $string = db_query('SELECT min(lid) AS lid, source FROM {locales_source} WHERE location LIKE :location', array( ':location' => '%.js%', ))->fetchObject(); - $edit = array('translations[fr][0]' => 'french translation'); - $this->drupalPost('admin/config/regional/translate/edit/' . $string->lid, $edit, t('Save translations')); + $edit = array('string' => $string->source); + $this->drupalPost('admin/config/regional/translate', $edit, t('Filter')); + $edit = array('strings[' . $string->lid . '][translations][0]' => 'french translation'); + $this->drupalPost('admin/config/regional/translate', $edit, t('Save translations')); _locale_rebuild_js('fr'); $locale_javascripts = variable_get('locale_translation_javascript', array()); $js_file = 'public://' . variable_get('locale_js_directory', 'languages') . '/fr_' . $locale_javascripts['fr'] . '.js'; diff --git a/core/modules/locale/locale-rtl.css b/core/modules/locale/locale-rtl.css index aaf1988..3ec5531 100644 --- a/core/modules/locale/locale-rtl.css +++ b/core/modules/locale/locale-rtl.css @@ -1,12 +1,11 @@ - #locale-translation-filter-form .form-item-language, #locale-translation-filter-form .form-item-translation, #locale-translation-filter-form .form-item-group { float: right; - padding-left: .8em; + padding-left: 1em; padding-right: 0; } #locale-translation-filter-form .form-actions { float: right; - padding: 3ex 1em 0 0; + padding: 3.5ex 0 0 0; } diff --git a/core/modules/locale/locale.admin.css b/core/modules/locale/locale.admin.css new file mode 100644 index 0000000..e8b3602 --- /dev/null +++ b/core/modules/locale/locale.admin.css @@ -0,0 +1,38 @@ +#locale-translate-filter-form .form-item-langcode, +#locale-translate-filter-form .form-item-translation, +#locale-translate-filter-form .form-item-customized { + float: left; /* LTR */ + padding-right: 1em; /* LTR */ + margin-bottom: 0; + /** + * In Opera 9, DOM elements with the property of "overflow: auto" + * will partially hide its contents with unnecessary scrollbars when + * its immediate child is floated without an explicit width set. + */ + width: 15em; +} +#locale-translate-filter-form .form-type-select select { + width: 100%; +} +#locale-translate-filter-form .form-actions { + float: left; /* LTR */ + padding: 3.5ex 0 0 0em; /* LTR */ +} +#locale-translate-edit-form th{ + width: 50%; + table-layout: fixed; +} +#locale-translate-edit-form tr .form-item{ + white-space: normal; +} +#locale-translate-edit-form td { + vertical-align: top +} + +#locale-translate-edit-form tr.changed { + background: #ffb; +} + +#locale-translate-edit-form .form-type-item abbr.ajax-changed { + position: absolute; +} diff --git a/core/modules/locale/locale.admin.js b/core/modules/locale/locale.admin.js new file mode 100644 index 0000000..f269f7a --- /dev/null +++ b/core/modules/locale/locale.admin.js @@ -0,0 +1,51 @@ +(function ($) { + +"use strict"; + +/** + * Markes changes of translations + */ +Drupal.behaviors.localeTranslateDirty = { + attach: function () { + var $form = $("#locale-translate-edit-form").once('localetranslatedirty'); + if ($form.length) { + // Display a notice if any row changed. + $form.one('change.localeTranslateDirty', function () { + var $marker = $(Drupal.theme('localeTranslateChangedWarning')).hide(); + $(this).addClass('changed').after($marker); + $marker.fadeIn('slow'); + }); + // Highlight changed row. + $form.on('change.localeTranslateDirty', 'tr', function (e) { + var + $row = $(this), + $rowToMark = $row.once('localemark'), + marker = Drupal.theme('localeTranslateChangedMarker'); + + $row.addClass('changed'); + // Add an asterisk only once if row changed. + if ($rowToMark.length) { + $rowToMark.find('td:first-child .form-item').append(marker); + } + }); + } + }, + detach: function (context, settings, trigger) { + if (trigger === 'unload') { + var $form = $("#locale-translate-edit-form").removeOnce('localetranslatedirty'); + if ($form.length) { + $form.off('change.localeTranslateDirty'); + } + } + } +}; + +Drupal.theme.prototype.localeTranslateChangedMarker = function () { + return '*'; +}; + +Drupal.theme.prototype.localeTranslateChangedWarning = function () { + return '
' + Drupal.theme('localeTranslateChangedMarker') + ' ' + Drupal.t('Changes made in this table will not be saved until the form is submitted.') + '
'; +}; + +})(jQuery); diff --git a/core/modules/locale/locale.css b/core/modules/locale/locale.css deleted file mode 100644 index 6c03945..0000000 --- a/core/modules/locale/locale.css +++ /dev/null @@ -1,25 +0,0 @@ -.locale-untranslated { - font-style: normal; - text-decoration: line-through; -} - -#locale-translation-filter-form .form-item-language, -#locale-translation-filter-form .form-item-translation, -#locale-translation-filter-form .form-item-customized { - float: left; /* LTR */ - padding-right: .8em; /* LTR */ - margin: 0.1em; - /** - * In Opera 9, DOM elements with the property of "overflow: auto" - * will partially hide its contents with unnecessary scrollbars when - * its immediate child is floated without an explicit width set. - */ - width: 15em; -} -#locale-translation-filter-form .form-type-select select { - width: 100%; -} -#locale-translation-filter-form .form-actions { - float: left; /* LTR */ - padding: 3ex 0 0 1em; /* LTR */ -} diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module index ae4c5f0..628f663 100644 --- a/core/modules/locale/locale.module +++ b/core/modules/locale/locale.module @@ -2,13 +2,13 @@ /** * @file - * Add language handling functionality and enables the translation of the - * user interface to languages other than English. + * Add language handling functionality and enables the translation of the + * user interface to languages other than English. * - * When enabled, multiple languages can be set up. The site interface - * can be displayed in different languages, as well as nodes can have languages - * assigned. The setup of languages and translations is completely web based. - * Gettext portable object files are supported. + * When enabled, multiple languages can be set up. The site interface + * can be displayed in different languages, as well as nodes can have languages + * assigned. The setup of languages and translations is completely web based. + * Gettext portable object files are supported. */ use Drupal\locale\LocaleLookup; @@ -64,9 +64,6 @@ const LOCALE_NOT_CUSTOMIZED = 0; */ const LOCALE_CUSTOMIZED = 1; -// --------------------------------------------------------------------------------- -// Hook implementations - /** * Implements hook_help(). */ @@ -89,7 +86,7 @@ function locale_help($path, $arg) { return $output; case 'admin/config/regional/language': - return '

' . t('Interface text can be translated. Download contributed translations from Drupal.org.', array('@translations' => 'http://localize.drupal.org')) . '

'; + return '

' . t('Interface text can be translated. Download contributed translations from Drupal.org.', array('@translations' => 'http://localize.drupal.org', '@translate' => url('admin/config/regional/translate'))) . '

'; case 'admin/config/regional/translate': $output = '

' . t('This page allows a translator to search for specific translated and untranslated strings, and is used when creating or editing translations. (Note: For translation tasks involving many strings, it may be more convenient to export strings for offline editing in a desktop Gettext translation editor.) Searches may be limited to strings in a specific language.', array('@export' => url('admin/config/regional/translate/export'))) . '

'; @@ -113,7 +110,7 @@ function locale_menu() { $items['admin/config/regional/translate'] = array( 'title' => 'User interface translation', 'description' => 'Translate the built-in user interface.', - 'page callback' => 'locale_translate_seek_screen', + 'page callback' => 'locale_translate_page', 'access arguments' => array('translate interface'), 'file' => 'locale.pages.inc', 'weight' => -5, @@ -141,20 +138,6 @@ function locale_menu() { 'type' => MENU_LOCAL_TASK, 'file' => 'locale.bulk.inc', ); - $items['admin/config/regional/translate/edit/%'] = array( - 'title' => 'Edit string', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('locale_translate_edit_form', 5), - 'access arguments' => array('translate interface'), - 'file' => 'locale.pages.inc', - ); - $items['admin/config/regional/translate/delete/%'] = array( - 'title' => 'Delete string', - 'page callback' => 'locale_translate_delete_page', - 'page arguments' => array(5), - 'access arguments' => array('translate interface'), - 'file' => 'locale.pages.inc', - ); // Localize date formats. $items['admin/config/regional/date-time/locale'] = array( @@ -267,6 +250,10 @@ function locale_theme() { 'locale_date_format_form' => array( 'render element' => 'form', ), + 'locale_translate_edit_form_strings' => array( + 'render element' => 'form', + 'file' => 'locale.pages.inc', + ), ); } @@ -292,11 +279,11 @@ function locale_field_language_alter(&$display_langcode, $context) { * behavior can be disabled by setting the 'locale_field_language_fallback' * variable to FALSE. * - * @param $field_langcodes + * @param array $field_langcodes * A reference to an array of language codes keyed by field name. - * @param $entity + * @param object $entity * The entity to be displayed. - * @param $langcode + * @param string $langcode * The language code $entity has to be displayed in. */ function locale_field_language_fallback(&$field_langcodes, $entity, $langcode) { @@ -368,26 +355,21 @@ function locale_language_delete($language) { // Changing the language settings impacts the interface: cache('page')->flush(); - // Clearing all locale cache from database + // Clearing all locale cache from database. cache()->delete('locale:' . $language->langcode); } - -// --------------------------------------------------------------------------------- -// Locale core functionality - /** * Provides interface translation services. - * * This function is called from t() to translate a string if needed. * - * @param $string + * @param string $string * A string to look up translation for. If omitted, all the * cached strings will be returned in all languages already * used on the page. - * @param $context + * @param string $context * The context of this string. - * @param $langcode + * @param string $langcode * Language code to use for the lookup. */ function locale($string = NULL, $context = NULL, $langcode = NULL) { @@ -402,7 +384,7 @@ function locale($string = NULL, $context = NULL, $langcode = NULL) { if (!isset($string)) { - // Return all cached strings if no string was specified + // Return all cached strings if no string was specified. return $locale_t; } @@ -428,12 +410,14 @@ function locale_reset() { * * The index is computed from the formula of this language. * - * @param $count + * @param int $count * Number to return plural for. - * @param $langcode + * + * @param string $langcode * Optional language code to translate to a language other than * what is used to display the page. - * @return + * + * @return int * The numeric index of the plural variant to use for this $langcode and * $count combination or -1 if the language was not found or does not have a * plural formula. @@ -499,7 +483,7 @@ function locale_themes_enabled($themes) { * This function will either import translation for the component change * right away, or start a batch if more files need to be imported. * - * @param $components + * @param array $components * An array of component (theme and/or module) names to import * translations for. * @@ -579,7 +563,7 @@ function locale_js_alter(&$javascript) { } /** - * Implement hook_library_info_alter(). + * Implements hook_library_info_alter(). * * Provides the language support for the jQuery UI Date Picker. */ @@ -636,13 +620,15 @@ function locale_form_language_admin_overview_form_alter(&$form, &$form_state) { ); if ($langcode != 'en' || locale_translate_english()) { $form['languages'][$langcode]['locale_statistics'] = array( - '#type' => 'link', - '#title' => t('@translated/@total (@ratio%)', array( - '@translated' => $stats[$langcode]['translated'], - '@total' => $total_strings, - '@ratio' => $stats[$langcode]['ratio'], - )), - '#href' => 'admin/config/regional/translate/translate', + '#markup' => l( + t('@translated/@total (@ratio%)', array( + '@translated' => $stats[$langcode]['translated'], + '@total' => $total_strings, + '@ratio' => $stats[$langcode]['ratio'], + )), + 'admin/config/regional/translate/translate', + array('query' => array('langcode' => $langcode)) + ), ); } else { @@ -654,7 +640,7 @@ function locale_form_language_admin_overview_form_alter(&$form, &$form_state) { } /** - * Implements hook_form_FORM_ID_alter() for language_admin_add_form((). + * Implements hook_form_FORM_ID_alter() for language_admin_add_form(). */ function locale_form_language_admin_add_form_alter(&$form, &$form_state) { $form['predefined_submit']['#submit'][] = 'locale_form_language_admin_add_form_alter_submit'; @@ -725,7 +711,7 @@ function locale_form_system_file_system_settings_alter(&$form, $form_state) { } /** - * Implements hook_preprocess_HOOK() for node.tpl.php. + * Implements hook_preprocess_HOOK(). */ function locale_preprocess_node(&$variables) { if ($variables['langcode'] != LANGUAGE_NOT_SPECIFIED) { @@ -836,7 +822,8 @@ function _locale_parse_js_file($filepath) { 'context' => $plural_matches[3][$key], ); - // If there is also a plural version of this string, add it to the strings array. + // If there is also a plural version of this string, + // add it to the strings array. if (isset($plural_matches[2][$key])) { $matches[] = array( 'string' => $plural_matches[2][$key], @@ -847,9 +834,8 @@ function _locale_parse_js_file($filepath) { // Loop through all matches and process them. foreach ($matches as $key => $match) { - // Remove the quotes and string concatenations from the string and context. - $string = implode('', preg_split('~(? $string, ':context' => $context))->fetchObject(); @@ -872,7 +858,8 @@ function _locale_parse_js_file($filepath) { } } else { - // We don't have the source string yet, thus we insert it into the database. + // We don't have the source string yet, thus we insert + // it into the database. db_insert('locales_source') ->fields(array( 'location' => $filepath, @@ -892,10 +879,10 @@ function _locale_parse_js_file($filepath) { * files are rebuilt (with locale_update_js_files()) the next time a * request is served in that language. * - * @param $langcode + * @param string $langcode * The language code for which the file needs to be refreshed. * - * @return + * @return array * New content of the 'javascript_parsed' variable. */ function _locale_invalidate_js($langcode = NULL) { @@ -923,7 +910,7 @@ function _locale_invalidate_js($langcode = NULL) { /** * (Re-)Creates the JavaScript translation file for a language. * - * @param $langcode + * @param string $langcode * The language, the translation file should be (re)created for. */ function _locale_rebuild_js($langcode = NULL) { @@ -965,7 +952,8 @@ function _locale_rebuild_js($langcode = NULL) { // There is (on purpose) no front end to edit that variable. $dir = 'public://' . variable_get('locale_js_directory', 'languages'); - // Delete old file, if we have no translations anymore, or a different file to be saved. + // Delete old file, if we have no translations anymore, + // or a different file to be saved. $locale_javascripts = variable_get('locale_translation_javascript', array()); $changed_hash = !isset($locale_javascripts[$language->langcode]) || ($locale_javascripts[$language->langcode] != $data_hash); if (!empty($locale_javascripts[$language->langcode]) && (!$data || $changed_hash)) { @@ -1040,12 +1028,12 @@ function _locale_rebuild_js($langcode = NULL) { /** * Save locale specific date formats to the database. * - * @param $langcode + * @param string $langcode * Language code, can be 2 characters, e.g. 'en' or 5 characters, e.g. * 'en-CA'. - * @param $type + * @param string $type * Date format type, e.g. 'short', 'medium'. - * @param $format + * @param string $format * The date format string. */ function locale_date_format_save($langcode, $type, $format) { @@ -1067,10 +1055,10 @@ function locale_date_format_save($langcode, $type, $format) { /** * Select locale date format details from database. * - * @param $languages + * @param array $languages * An array of language codes. * - * @return + * @return array * An array of date formats. */ function locale_get_localized_date_format($languages) { diff --git a/core/modules/locale/locale.pages.inc b/core/modules/locale/locale.pages.inc index 726731d..6ef02a1 100644 --- a/core/modules/locale/locale.pages.inc +++ b/core/modules/locale/locale.pages.inc @@ -10,155 +10,89 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * String search screen. */ -function locale_translate_seek_screen() { - // Add CSS. - drupal_add_css(drupal_get_path('module', 'locale') . '/locale.css'); - - $elements = drupal_get_form('locale_translation_filter_form'); - $output = drupal_render($elements); - $output .= _locale_translate_seek(); - return $output; +function locale_translate_page() { + return array( + 'filter' => drupal_get_form('locale_translate_filter_form'), + 'form' => drupal_get_form('locale_translate_edit_form'), + ); } /** - * Perform a string search and display results in a table + * Build a string search query. */ -function _locale_translate_seek() { - $output = ''; - - // We have at least one criterion to match - if (!($query = _locale_translate_seek_query())) { - $query = array( - 'translation' => 'all', - 'language' => 'all', - 'customized' => 'all', - 'string' => '', - ); - } +function locale_translate_query() { + $filter_values = locale_translate_filter_values(); $sql_query = db_select('locales_source', 's'); - $sql_query->leftJoin('locales_target', 't', 't.lid = s.lid'); + // Language is sanitized to be one of the possible options in + // locale_translate_filter_values(). + $sql_query->leftJoin('locales_target', 't', "t.lid = s.lid AND t.language = :langcode", array(':langcode' => $filter_values['langcode'])); $sql_query->fields('s', array('source', 'location', 'context', 'lid')); $sql_query->fields('t', array('translation', 'language', 'customized')); - // Compute LIKE section. - switch ($query['translation']) { + if (!empty($filter_values['string'])) { + $sql_query->condition(db_or() + ->condition('s.source', '%' . db_like($filter_values['string']) . '%', 'LIKE') + ->condition('t.translation', '%' . db_like($filter_values['string']) . '%', 'LIKE') + ); + } + + // Add translation status conditions. + switch ($filter_values['translation']) { case 'translated': - $sql_query->condition('t.translation', '%' . db_like($query['string']) . '%', 'LIKE'); - $sql_query->orderBy('t.translation', 'DESC'); - if ($query['customized'] != 'all') { - $sql_query->condition('t.customized', $query['customized']); + $sql_query->isNotNull('t.translation'); + if ($filter_values['customized'] != 'all') { + $sql_query->condition('t.customized', $filter_values['customized']); } break; + case 'untranslated': - $sql_query->condition(db_and() - ->condition('s.source', '%' . db_like($query['string']) . '%', 'LIKE') - ->isNull('t.translation') - ); - $sql_query->orderBy('s.source'); - break; - case 'all' : - default: - $condition = db_or() - ->condition('s.source', '%' . db_like($query['string']) . '%', 'LIKE'); - if ($query['language'] != LANGUAGE_SYSTEM) { - // Only search in translations if the language is not forced to system language. - $condition->condition('t.translation', '%' . db_like($query['string']) . '%', 'LIKE'); - } - $sql_query->condition($condition); + $sql_query->isNull('t.translation'); break; - } - - $limit_language = NULL; - if ($query['language'] != LANGUAGE_SYSTEM && $query['language'] != 'all') { - $sql_query->condition('language', $query['language']); - $limit_language = $query['language']; - } - - $sql_query = $sql_query - ->extend('Drupal\Core\Database\Query\PagerSelectExtender') - ->limit(50); - $locales = $sql_query->execute(); - - $header = array(t('String'), t('Context'), ($limit_language) ? t('Language') : t('Languages'), array('data' => t('Operations'), 'colspan' => '2')); - - $strings = array(); - foreach ($locales as $locale) { - if (!isset($strings[$locale->lid])) { - $strings[$locale->lid] = array( - 'languages' => array(), - 'location' => $locale->location, - 'source' => $locale->source, - 'context' => $locale->context, - ); - } - if (isset($locale->language)) { - $strings[$locale->lid]['languages'][$locale->language] = $locale->translation; - } - } - $rows = array(); - foreach ($strings as $lid => $string) { - $rows[] = array( - array('data' => check_plain(truncate_utf8(str_replace(LOCALE_PLURAL_DELIMITER, ', ', $string['source']), 150, FALSE, TRUE)) . '
' . $string['location'] . ''), - $string['context'], - array('data' => _locale_translate_language_list($string['languages'], $limit_language), 'align' => 'center'), - array('data' => l(t('edit'), "admin/config/regional/translate/edit/$lid", array('query' => drupal_get_destination())), 'class' => array('nowrap')), - array('data' => l(t('delete'), "admin/config/regional/translate/delete/$lid", array('query' => drupal_get_destination())), 'class' => array('nowrap')), - ); } - $output .= theme('table', array('header' => $header, 'rows' => $rows, 'empty' => t('No strings available.'))); - $output .= theme('pager'); - - return $output; + $sql_query = $sql_query->extend('Drupal\Core\Database\Query\PagerSelectExtender')->limit(30); + return $sql_query->execute(); } /** - * List languages in search result table + * Build array out of search criteria specified in request variables. */ -function _locale_translate_language_list($translation, $limit_language) { - // Add CSS. - drupal_add_css(drupal_get_path('module', 'locale') . '/locale.css'); - - $languages = language_list(); - if (!locale_translate_english()) { - unset($languages['en']); - } - $output = ''; - foreach ($languages as $langcode => $language) { - if (!$limit_language || $limit_language == $langcode) { - $output .= (!empty($translation[$langcode])) ? $langcode . ' ' : "$langcode "; - } - } - - return $output; -} - -/** - * Build array out of search criteria specified in request variables - */ -function _locale_translate_seek_query() { - $query = &drupal_static(__FUNCTION__); - if (!isset($query)) { - $query = array(); - $fields = array('string', 'language', 'translation', 'customized'); - foreach ($fields as $field) { - if (isset($_SESSION['locale_translation_filter'][$field])) { - $query[$field] = $_SESSION['locale_translation_filter'][$field]; +function locale_translate_filter_values() { + $filter_values = &drupal_static(__FUNCTION__); + if (!isset($filter_values)) { + $filter_values = array(); + $filters = locale_translate_filters(); + foreach ($filters as $key => $filter) { + $filter_values[$key] = $filter['default']; + // Let the filter defaults be overwritten by parameters in the URL. + if (isset($_GET[$key])) { + // Only allow this value if it was among the options, or + // if there were no fixed options to filter for. + if (!isset($filter['options']) || isset($filter['options'][$_GET[$key]])) { + $filter_values[$key] = $_GET[$key]; + } + } + elseif (isset($_SESSION['locale_translate_filter'][$key])) { + // Only allow this value if it was among the options, or + // if there were no fixed options to filter for. + if (!isset($filter['options']) || isset($filter['options'][$_SESSION['locale_translate_filter'][$key]])) { + $filter_values[$key] = $_SESSION['locale_translate_filter'][$key]; + } } } } - return $query; + return $filter_values; } /** * List locale translation filters that can be applied. */ -function locale_translation_filters() { +function locale_translate_filters() { $filters = array(); - // Get all languages, except English + // Get all languages, except English. drupal_static_reset('language_list'); $languages = language_list(); $language_options = array(); @@ -168,14 +102,23 @@ function locale_translation_filters() { } } + // Pick the current interface language code for the filter. + $default_langcode = drupal_container()->get(LANGUAGE_TYPE_INTERFACE)->langcode; + if (!isset($language_options[$default_langcode])) { + $available_langcodes = array_keys($language_options); + $default_langcode = array_shift($available_langcodes); + } + $filters['string'] = array( 'title' => t('String contains'), 'description' => t('Leave blank to show all strings. The search is case sensitive.'), + 'default' => '', ); - $filters['language'] = array( - 'title' => t('Language'), - 'options' => array_merge(array('all' => t('All languages'), LANGUAGE_SYSTEM => t('System (English)')), $language_options), + $filters['langcode'] = array( + 'title' => t('Translation language'), + 'options' => $language_options, + 'default' => $default_langcode, ); $filters['translation'] = array( @@ -183,8 +126,9 @@ function locale_translation_filters() { 'options' => array( 'all' => t('Both translated and untranslated strings'), 'translated' => t('Only translated strings'), - 'untranslated' => t('Untranslated strings') + 'untranslated' => t('Only Untranslated strings'), ), + 'default' => 'all', ); $filters['customized'] = array( @@ -197,8 +141,9 @@ function locale_translation_filters() { 'states' => array( 'visible' => array( ':input[name=translation]' => array('value' => 'translated'), - ) + ), ), + 'default' => 'all', ); return $filters; @@ -209,8 +154,13 @@ function locale_translation_filters() { * * @ingroup forms */ -function locale_translation_filter_form() { - $filters = locale_translation_filters(); +function locale_translate_filter_form($form, &$form_state) { + $filters = locale_translate_filters(); + $filter_values = locale_translate_filter_values(); + + $form['#attached']['css'] = array( + drupal_get_path('module', 'locale') . '/locale.admin.css', + ); $form['filters'] = array( '#type' => 'fieldset', @@ -225,24 +175,24 @@ function locale_translation_filter_form() { '#type' => 'search', '#title' => $filter['title'], '#description' => $filter['description'], + '#default_value' => $filter_values[$key], ); } else { + $empty_option = isset($filter['options'][$filter['default']]) ? $filter['options'][$filter['default']] : ''; $form['filters']['status'][$key] = array( '#title' => $filter['title'], '#type' => 'select', - '#empty_value' => 'all', - '#empty_option' => $filter['options']['all'], + '#empty_value' => $filter['default'], + '#empty_option' => $empty_option, '#size' => 0, '#options' => $filter['options'], + '#default_value' => $filter_values[$key], ); if (isset($filter['states'])) { $form['filters']['status'][$key]['#states'] = $filter['states']; } } - if (!empty($_SESSION['locale_translation_filter'][$key])) { - $form['filters']['status'][$key]['#default_value'] = $_SESSION['locale_translation_filter'][$key]; - } } $form['filters']['actions'] = array( @@ -253,10 +203,10 @@ function locale_translation_filter_form() { '#type' => 'submit', '#value' => t('Filter'), ); - if (!empty($_SESSION['locale_translation_filter'])) { + if (!empty($_SESSION['locale_translate_filter'])) { $form['filters']['actions']['reset'] = array( '#type' => 'submit', - '#value' => t('Reset') + '#value' => t('Reset'), ); } @@ -264,168 +214,159 @@ function locale_translation_filter_form() { } /** - * Validate result from locale translation filter form. - */ -function locale_translation_filter_form_validate($form, &$form_state) { - if ($form_state['values']['op'] == t('Filter') && empty($form_state['values']['language'])) { - form_set_error('type', t('You must select something to filter by.')); - } -} - -/** * Process result from locale translation filter form. */ -function locale_translation_filter_form_submit($form, &$form_state) { +function locale_translate_filter_form_submit($form, &$form_state) { $op = $form_state['values']['op']; - $filters = locale_translation_filters(); + $filters = locale_translate_filters(); switch ($op) { case t('Filter'): foreach ($filters as $name => $filter) { if (isset($form_state['values'][$name])) { - $_SESSION['locale_translation_filter'][$name] = $form_state['values'][$name]; + $_SESSION['locale_translate_filter'][$name] = $form_state['values'][$name]; } } break; + case t('Reset'): - $_SESSION['locale_translation_filter'] = array(); + $_SESSION['locale_translate_filter'] = array(); break; + } $form_state['redirect'] = 'admin/config/regional/translate/translate'; } - /** - * User interface for string editing. + * User interface for string editing as one table. * * @ingroup forms */ -function locale_translate_edit_form($form, &$form_state, $lid) { - // Fetch source string, if possible. - $source = db_query('SELECT source, context, location FROM {locales_source} WHERE lid = :lid', array(':lid' => $lid))->fetchObject(); - if (!$source) { - drupal_set_message(t('String not found.'), 'error'); - drupal_goto('admin/config/regional/translate/translate'); - } - // Split source to work with plural values. - $source_array = explode(LOCALE_PLURAL_DELIMITER, $source->source); - if (count($source_array) == 1) { - // Add original text value and mark as non-plural. - $form['plural'] = array( - '#type' => 'value', - '#value' => 0 - ); - $form['original'] = array( - '#type' => 'item', - '#title' => t('Original text'), - '#markup' => check_plain($source_array[0]), - ); - } - else { - // Add original text value and mark as plural. - $form['plural'] = array( - '#type' => 'value', - '#value' => 1 - ); - $form['original_singular'] = array( - '#type' => 'item', - '#title' => t('Original singular form'), - '#markup' => check_plain($source_array[0]), - ); - $form['original_plural'] = array( - '#type' => 'item', - '#title' => t('Original plural form'), - '#markup' => check_plain($source_array[1]), - ); - } - if (!empty($source->context)) { - $form['context'] = array( - '#type' => 'item', - '#title' => t('Context'), - '#markup' => check_plain($source->context), - ); - } - $form['lid'] = array( - '#type' => 'value', - '#value' => $lid +function locale_translate_edit_form($form, &$form_state) { + $filter_values = locale_translate_filter_values(); + $langcode = $filter_values['langcode']; + + drupal_static_reset('language_list'); + $languages = language_list(); + + $langname = isset($langcode) ? $languages[$langcode]->name : ""; + + $path = drupal_get_path('module', 'locale'); + $form['#attached']['css'] = array( + $path . '/locale.admin.css', ); - $form['location'] = array( - '#type' => 'value', - '#value' => $source->location + $form['#attached']['js'] = array( + $path . '/locale.admin.js', ); - // Include default form controls with empty values for all languages. - // This ensures that the languages are always in the same order in forms. - $languages = language_list(); - if (!locale_translate_english()) { - unset($languages['en']); - } - // Store languages to iterate for validation and submission of the form. - $form_state['langcodes'] = array_keys($languages); - $plural_formulas = variable_get('locale_translation_plurals', array()); + $form['langcode'] = array( + '#type' => 'value', + '#value' => $filter_values['langcode'], + ); - $form['translations'] = array( - '#type' => 'vertical_tabs', - '#tree' => TRUE + $form['strings'] = array( + '#type' => 'item', + '#tree' => TRUE, + '#language' => $langname, + '#theme' => 'locale_translate_edit_form_strings', ); - // Approximate the number of rows to use in the default textarea. - $rows = min(ceil(str_word_count($source_array[0]) / 12), 10); - foreach ($languages as $langcode => $language) { - $form['translations'][$langcode] = array( - '#type' => 'fieldset', - '#title' => $language->name, - ); - if (empty($form['plural']['#value'])) { - $form['translations'][$langcode][0] = array( - '#type' => 'textarea', - '#title' => $language->name, - '#rows' => $rows, - '#default_value' => '', - ); - } - else { - // Dealing with plural strings. - if (isset($plural_formulas[$langcode]['plurals']) && $plural_formulas[$langcode]['plurals'] > 2) { - // Add a textarea for each plural variant. - for ($i = 0; $i < $plural_formulas[$langcode]['plurals']; $i++) { - $form['translations'][$langcode][$i] = array( - '#type' => 'textarea', - '#title' => ($i == 0 ? t('Singular form') : format_plural($i, 'First plural form', '@count. plural form')), - '#rows' => $rows, - '#default_value' => '', - ); - } + if (isset($langcode)) { + $strings = locale_translate_query(); + + $plural_formulas = variable_get('locale_translation_plurals', array()); + + foreach ($strings as $string) { + // Split source to work with plural values. + $source_array = explode(LOCALE_PLURAL_DELIMITER, $string->source); + $translation_array = explode(LOCALE_PLURAL_DELIMITER, $string->translation); + if (count($source_array) == 1) { + // Add original string value and mark as non-plural. + $form['strings'][$string->lid]['plural'] = array( + '#type' => 'value', + '#value' => 0, + ); + $form['strings'][$string->lid]['original'] = array( + '#type' => 'item', + '#title' => t('Source string'), + '#title_display' => 'invisible', + '#markup' => check_plain($source_array[0]), + ); } else { - // Fallback for unknown number of plurals. - $form['translations'][$langcode][0] = array( - '#type' => 'textarea', + // Add original string value and mark as plural. + $form['strings'][$string->lid]['plural'] = array( + '#type' => 'value', + '#value' => 1, + ); + $form['strings'][$string->lid]['original_singular'] = array( + '#type' => 'item', '#title' => t('Singular form'), - '#rows' => $rows, - '#default_value' => '', + '#markup' => check_plain($source_array[0]), ); - $form['translations'][$langcode][1] = array( - '#type' => 'textarea', + $form['strings'][$string->lid]['original_plural'] = array( + '#type' => 'item', '#title' => t('Plural form'), + '#markup' => check_plain($source_array[1]), + ); + } + if (!empty($string->context)) { + $form['strings'][$string->lid]['context'] = array( + '#type' => 'value', + '#value' => check_plain($string->context), + ); + } + $form['strings'][$string->lid]['location'] = array( + '#type' => 'value', + '#value' => $string->location, + ); + + // Approximate the number of rows to use in the default textarea. + $rows = min(ceil(str_word_count($source_array[0]) / 12), 10); + if (empty($form['strings'][$string->lid]['plural']['#value'])) { + $form['strings'][$string->lid]['translations'][0] = array( + '#type' => 'textarea', + '#title' => t('Translated string'), + '#title_display' => 'invisible', '#rows' => $rows, - '#default_value' => '', + '#default_value' => $translation_array[0], ); } + else { + // Dealing with plural strings. + if (isset($plural_formulas[$langcode]['plurals']) && $plural_formulas[$langcode]['plurals'] > 2) { + // Add a textarea for each plural variant. + for ($i = 0; $i < $plural_formulas[$langcode]['plurals']; $i++) { + $form['strings'][$string->lid]['translations'][$i] = array( + '#type' => 'textarea', + '#title' => ($i == 0 ? t('Singular form') : format_plural($i, 'First plural form', '@count. plural form')), + '#rows' => $rows, + '#default_value' => isset($translation_array[$i]) ? $translation_array[$i] : '', + ); + } + } + else { + // Fallback for unknown number of plurals. + $form['strings'][$string->lid]['translations'][0] = array( + '#type' => 'textarea', + '#title' => t('Singular form'), + '#rows' => $rows, + '#default_value' => $translation_array[0], + ); + $form['strings'][$string->lid]['translations'][1] = array( + '#type' => 'textarea', + '#title' => t('Plural form'), + '#rows' => $rows, + '#default_value' => isset($translation_array[1]) ? $translation_array[1] : '', + ); + } + } } - } - - // Fetch translations and fill in default values in the form. - $result = db_query("SELECT DISTINCT translation, language FROM {locales_target} WHERE lid = :lid", array(':lid' => $lid)); - foreach ($result as $translation) { - $translation_array = explode(LOCALE_PLURAL_DELIMITER, $translation->translation); - for ($i = 0; $i < count($translation_array); $i++) { - $form['translations'][$translation->language][$i]['#default_value'] = $translation_array[$i]; + if (count(element_children($form['strings']))) { + $form['actions'] = array('#type' => 'actions'); + $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save translations')); } } - - $form['actions'] = array('#type' => 'actions'); - $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save translations')); return $form; } @@ -433,9 +374,11 @@ function locale_translate_edit_form($form, &$form_state, $lid) { * Validate string editing form submissions. */ function locale_translate_edit_form_validate($form, &$form_state) { - foreach ($form_state['langcodes'] as $langcode) { - foreach ($form_state['values']['translations'][$langcode] as $key => $value) { + $langcode = $form_state['values']['langcode']; + foreach ($form_state['values']['strings'] as $lid => $translations) { + foreach ($translations['translations'] as $key => $value) { if (!locale_string_is_safe($value)) { + form_set_error("strings][$lid][translations][$key", t('The submitted string contains disallowed HTML: %string', array('%string' => $value))); form_set_error("translations][$langcode][$key", t('The submitted string contains disallowed HTML: %string', array('%string' => $value))); watchdog('locale', 'Attempted submission of a translation string with disallowed HTML: %string', array('%string' => $value), WATCHDOG_WARNING); } @@ -445,18 +388,16 @@ function locale_translate_edit_form_validate($form, &$form_state) { /** * Process string editing form submissions. - * - * Saves all translations of one string submitted from a form. */ function locale_translate_edit_form_submit($form, &$form_state) { - $lid = $form_state['values']['lid']; - foreach ($form_state['langcodes'] as $langcode) { + $langcode = $form_state['values']['langcode']; + foreach ($form_state['values']['strings'] as $lid => $translations) { // Serialize plural variants in one string by LOCALE_PLURAL_DELIMITER. - $value = implode(LOCALE_PLURAL_DELIMITER, $form_state['values']['translations'][$langcode]); - $translation = db_query("SELECT translation FROM {locales_target} WHERE lid = :lid AND language = :language", array(':lid' => $lid, ':language' => $langcode))->fetchField(); + $translation_new = implode(LOCALE_PLURAL_DELIMITER, $translations['translations']); + $translation_old = db_query("SELECT translation FROM {locales_target} WHERE lid = :lid AND language = :language", array(':lid' => $lid, ':language' => $langcode))->fetchField(); // No translation when all strings are empty. $has_translation = FALSE; - foreach ($form_state['values']['translations'][$langcode] as $string) { + foreach ($translations['translations'] as $string) { if (!empty($string)) { $has_translation = TRUE; break; @@ -464,28 +405,28 @@ function locale_translate_edit_form_submit($form, &$form_state) { } if ($has_translation) { // Only update or insert if we have a value to use. - if (!empty($translation) && $translation != $value) { + if (!empty($translation_old) && $translation_old != $translation_new) { db_update('locales_target') ->fields(array( - 'translation' => $value, + 'translation' => $translation_new, 'customized' => LOCALE_CUSTOMIZED, )) ->condition('lid', $lid) ->condition('language', $langcode) ->execute(); } - if (empty($translation)) { + if (empty($translation_old)) { db_insert('locales_target') ->fields(array( 'lid' => $lid, - 'translation' => $value, + 'translation' => $translation_new, 'language' => $langcode, 'customized' => LOCALE_CUSTOMIZED, )) ->execute(); } } - elseif (!empty($translation)) { + elseif (!empty($translation_old)) { // Empty translation entered: remove existing entry from database. db_delete('locales_target') ->condition('lid', $lid) @@ -493,55 +434,51 @@ function locale_translate_edit_form_submit($form, &$form_state) { ->execute(); } - // Force JavaScript translation file recreation for this language. - _locale_invalidate_js($langcode); } - drupal_set_message(t('The string has been saved.')); + drupal_set_message(t('The strings have been saved.')); + + // Keep the user on the current pager page. + if (isset($_GET['page'])) { + $form_state['redirect'] = array('admin/config/regional/translate', array('query' => array('page' => $_GET['page']))); + } + // Force JavaScript translation file recreation for this language. + _locale_invalidate_js($langcode); // Clear locale cache. - _locale_invalidate_js(); cache()->deletePrefix('locale:'); - - $form_state['redirect'] = 'admin/config/regional/translate/translate'; - return; } /** - * String deletion confirmation page. + * Default theme function for translatione edit form. */ -function locale_translate_delete_page($lid) { - if ($source = db_query('SELECT lid, source FROM {locales_source} WHERE lid = :lid', array(':lid' => $lid))->fetchObject()) { - return drupal_get_form('locale_translate_delete_form', $source); - } - else { - throw new NotFoundHttpException(); +function theme_locale_translate_edit_form_strings($variables) { + $output = ''; + $form = $variables['form']; + $header = array( + t('Source string'), + t('Translation for @language', array('@language' => $form['#language'])), + ); + $rows = array(); + foreach (element_children($form) as $lid) { + $string = $form[$lid]; + if ($string['plural']['#value']) { + $source = drupal_render($string['original_singular']) . '
' . drupal_render($string['original_plural']); + } + else { + $source = drupal_render($string['original']); + } + $source .= empty($string['context']) ? '' : '
' . t('In Context') . ': ' . $string['context']['#value'] . ''; + $rows[] = array( + array('data' => $source), + array('data' => $string['translations']), + ); } -} - -/** - * User interface for the string deletion confirmation screen. - * - * @ingroup forms - */ -function locale_translate_delete_form($form, &$form_state, $source) { - $form['lid'] = array('#type' => 'value', '#value' => $source->lid); - return confirm_form($form, t('Are you sure you want to delete the string "%source"?', array('%source' => $source->source)), 'admin/config/regional/translate/translate', t('Deleting the string will remove all translations of this string in all languages. This action cannot be undone.'), t('Delete'), t('Cancel')); -} - -/** - * Process string deletion submissions. - */ -function locale_translate_delete_form_submit($form, &$form_state) { - db_delete('locales_source') - ->condition('lid', $form_state['values']['lid']) - ->execute(); - db_delete('locales_target') - ->condition('lid', $form_state['values']['lid']) - ->execute(); - // Force JavaScript translation file recreation for all languages. - _locale_invalidate_js(); - cache()->deletePrefix('locale:'); - drupal_set_message(t('The string has been removed.')); - $form_state['redirect'] = 'admin/config/regional/translate/translate'; + $output .= theme('table', array( + 'header' => $header, + 'rows' => $rows, + 'empty' => t('No strings available.')) + ); + $output .= theme('pager'); + return $output; } diff --git a/core/modules/translation/translation.js b/core/modules/translation/translation.js new file mode 100644 index 0000000..b36f5c7 --- /dev/null +++ b/core/modules/translation/translation.js @@ -0,0 +1,21 @@ +(function ($) { + +"use strict"; + +Drupal.behaviors.TranslationEnable = { + attach: function (context) { + $('#edit-node-type-language-default, #edit-node-type-language-locked', context).change(function(context) { + var default_language = $('#edit-node-type-language-default').val(); + + if ((default_language == 'und' || default_language == 'zxx' || default_language == 'mul') && $('#edit-node-type-language-locked').attr('checked')) { + $('.form-item-node-type-language-translation-enabled').hide(); + $('#edit-node-type-language-translation-enabled').removeAttr('checked'); + } else { + $('.form-item-node-type-language-translation-enabled').show(); + } + }); + $('#edit-node-type-language-default', context).trigger('change'); + } +}; + +})(jQuery); \ No newline at end of file