diff --git a/core/modules/config_translation/migrations/d7_menu_translation.yml b/core/modules/config_translation/migrations/d7_menu_translation.yml new file mode 100644 index 0000000000..129ad18074 --- /dev/null +++ b/core/modules/config_translation/migrations/d7_menu_translation.yml @@ -0,0 +1,31 @@ +id: d7_menu_translation +label: Menu translation +migration_tags: + - Drupal 7 + - Configuration + - Multilingual +source: + plugin: d7_menu_translation +process: + id: + - + plugin: migration_lookup + migration: d7_menu + source: menu_name + - + plugin: skip_on_empty + method: row + langcode: language + property: + plugin: static_map + source: property + map: + title: label + description: description + translation: translation +destination: + plugin: entity:menu + destination_module: config_translation +migration_dependencies: + required: + - d7_menu diff --git a/core/modules/config_translation/migrations/state/config_translation.migrate_drupal.yml b/core/modules/config_translation/migrations/state/config_translation.migrate_drupal.yml index 5c7b5fddc9..52552137a1 100644 --- a/core/modules/config_translation/migrations/state/config_translation.migrate_drupal.yml +++ b/core/modules/config_translation/migrations/state/config_translation.migrate_drupal.yml @@ -4,6 +4,7 @@ finished: 7: i18n_variable: config_translation i18n_taxonomy: config_translation + i18n_menu: config_translation not_finished: 6: # language content comment settings. diff --git a/core/modules/migrate_drupal/tests/fixtures/drupal7.php b/core/modules/migrate_drupal/tests/fixtures/drupal7.php index 372ffa6d0b..1c05c5dbab 100644 --- a/core/modules/migrate_drupal/tests/fixtures/drupal7.php +++ b/core/modules/migrate_drupal/tests/fixtures/drupal7.php @@ -19382,6 +19382,36 @@ 'objectindex' => '0', 'format' => '', )) +->values(array( + 'lid' => '800', + 'textgroup' => 'menu', + 'context' => 'menu:main-menu:title', + 'objectid' => 'main-menu', + 'type' => 'menu', + 'property' => 'title', + 'objectindex' => '0', + 'format' => '', +)) +->values(array( + 'lid' => '801', + 'textgroup' => 'menu', + 'context' => 'menu:main-menu:description', + 'objectid' => 'main-menu', + 'type' => 'menu', + 'property' => 'description', + 'objectindex' => '0', + 'format' => '', +)) +->values(array( + 'lid' => '802', + 'textgroup' => 'menu', + 'context' => 'menu:menu-test-menu:description', + 'objectid' => 'menu-test-menu', + 'type' => 'menu', + 'property' => 'description', + 'objectindex' => '0', + 'format' => '', +)) ->execute(); $connection->schema()->createTable('i18n_translation_set', array( 'fields' => array( @@ -20918,6 +20948,46 @@ 'plural' => '0', 'i18n_status' => '0', )) +->values(array( + 'lid' => '800', + 'translation' => 'is - Main menu', + 'language' => 'is', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', +)) +->values(array( + 'lid' => '801', + 'translation' => 'is - Main menu description', + 'language' => 'is', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', +)) +->values(array( + 'lid' => '800', + 'translation' => 'fr - Main menu', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', +)) +->values(array( + 'lid' => '801', + 'translation' => 'fr - Main menu description', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', +)) +->values(array( + 'lid' => '802', + 'translation' => 'fr - Test menu description', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', +)) ->values(array( 'lid' => '765', 'translation' => 'Hár', @@ -20942,6 +21012,13 @@ 'plural' => '0', 'i18n_status' => '0', )) +->values(array( + 'menu_name' => 'menu-fixedlang', + 'title' => 'FixedLang', + 'description' => '', + 'language' => 'is', + 'i18n_mode' => '2', +)) ->values(array( 'lid' => '768', 'translation' => 'Rating', @@ -32505,6 +32582,35 @@ 'weight' => '0', 'include_file' => 'modules/filter/filter.admin.inc', )) +->values(array( + 'menu_name' => 'management', + 'mlid' => '535', + 'plid' => '44', + 'link_path' => 'admin/structure/menu/manage/menu-fixedlang', + 'router_path' => 'admin/structure/menu/manage/%', + 'link_title' => 'FixedLang', + 'options' => 'a:0:{}', + 'module' => 'menu', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '4', + 'customized' => '0', + 'p1' => '1', + 'p2' => '20', + 'p3' => '44', + 'p4' => '535', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', + 'language' => 'und', + 'i18n_tsid' => '0', +)) ->values(array( 'path' => 'admin/config/date', 'load_functions' => '', diff --git a/core/modules/migrate_drupal_ui/tests/src/Functional/d7/Upgrade7Test.php b/core/modules/migrate_drupal_ui/tests/src/Functional/d7/Upgrade7Test.php index 9625cb3a41..43c200c5b1 100644 --- a/core/modules/migrate_drupal_ui/tests/src/Functional/d7/Upgrade7Test.php +++ b/core/modules/migrate_drupal_ui/tests/src/Functional/d7/Upgrade7Test.php @@ -100,7 +100,7 @@ protected function getEntityCounts() { 'shortcut' => 6, 'shortcut_set' => 2, 'action' => 19, - 'menu' => 6, + 'menu' => 7, 'taxonomy_term' => 24, 'taxonomy_vocabulary' => 7, 'path_alias' => 8, diff --git a/core/modules/system/migrations/d7_menu.yml b/core/modules/system/migrations/d7_menu.yml index ce97f656b1..585319358d 100644 --- a/core/modules/system/migrations/d7_menu.yml +++ b/core/modules/system/migrations/d7_menu.yml @@ -17,5 +17,9 @@ process: user-menu: account label: title description: description + langcode: + plugin: default_value + source: language + default_value: und destination: plugin: entity:menu diff --git a/core/modules/system/src/Plugin/migrate/source/Menu.php b/core/modules/system/src/Plugin/migrate/source/Menu.php index a97878fa4a..14296f1ade 100644 --- a/core/modules/system/src/Plugin/migrate/source/Menu.php +++ b/core/modules/system/src/Plugin/migrate/source/Menu.php @@ -25,11 +25,19 @@ public function query() { * {@inheritdoc} */ public function fields() { - return [ + $fields = [ 'menu_name' => $this->t('The menu name. Primary key.'), 'title' => $this->t('The human-readable name of the menu.'), 'description' => $this->t('A description of the menu'), ]; + + if ($this->database->schema()->fieldExists('menu_custom', 'language')) { + $fields += [ + 'language' => $this->t('Menu language.'), + 'i8n_mode' => $this->t('Menu i18n mode.'), + ]; + } + return $fields; } /** diff --git a/core/modules/system/src/Plugin/migrate/source/d7/MenuTranslation.php b/core/modules/system/src/Plugin/migrate/source/d7/MenuTranslation.php new file mode 100644 index 0000000000..534e47e546 --- /dev/null +++ b/core/modules/system/src/Plugin/migrate/source/d7/MenuTranslation.php @@ -0,0 +1,83 @@ +fields('i18n', [ + 'lid', + 'textgroup', + 'context', + 'objectid', + 'type', + 'property', + 'objectindex', + 'format', + ]) + ->fields('lt', [ + 'lid', + 'translation', + 'language', + 'plid', + 'plural', + 'i18n_status', + ]) + ->condition('i18n.textgroup', 'menu') + ->isNotNull('lt.lid'); + + $query->addField('m', 'language', 'm_language'); + $query->leftJoin('i18n_string', 'i18n', 'i18n.objectid = m.menu_name'); + $query->leftJoin('locales_target', 'lt', 'lt.lid = i18n.lid'); + + return $query; + } + + /** + * {@inheritdoc} + */ + public function fields() { + $fields = [ + 'lid' => $this->t('Language string ID'), + 'language' => $this->t('Menu language'), + 'textgroup' => $this->t('A module defined group of translations'), + 'context' => $this->t('Full string ID for quick search: type:objectid:property.'), + 'objectid' => $this->t('Object ID'), + 'type' => $this->t('Object type for this string'), + 'property' => $this->t('Object property for this string'), + 'objectindex' => $this->t('Integer value of Object ID'), + 'format' => $this->t('The {filter_format}.format of the string'), + 'translation' => $this->t('Translation'), + 'plid' => $this->t('Parent lid'), + 'i18n_status' => $this->t('Translation needs update'), + ] + parent::fields(); + return $fields; + } + + /** + * {@inheritdoc} + */ + public function getIds() { + $ids = parent::getIds(); + $ids['language']['type'] = 'string'; + $ids['language']['alias'] = 'lt'; + $ids['property']['type'] = 'string'; + return $ids; + } + +} diff --git a/core/modules/system/tests/src/Kernel/Migrate/d7/MigrateMenuTest.php b/core/modules/system/tests/src/Kernel/Migrate/d7/MigrateMenuTest.php index eb80f42a09..b0cd4ff2a9 100644 --- a/core/modules/system/tests/src/Kernel/Migrate/d7/MigrateMenuTest.php +++ b/core/modules/system/tests/src/Kernel/Migrate/d7/MigrateMenuTest.php @@ -26,14 +26,17 @@ protected function setUp(): void { * * @param $id * The menu ID. + * @param string $language + * The menu language. * @param $label * The menu label. * @param $description * The menu description. */ - protected function assertEntity($id, $label, $description) { + protected function assertEntity($id, $language, $label, $description) { $navigation_menu = Menu::load($id); $this->assertSame($id, $navigation_menu->id()); + $this->assertSame($language, $navigation_menu->language()->getId()); $this->assertSame($label, $navigation_menu->label()); $this->assertSame($description, $navigation_menu->getDescription()); } @@ -42,11 +45,12 @@ protected function assertEntity($id, $label, $description) { * Tests the Drupal 7 menu to Drupal 8 migration. */ public function testMenu() { - $this->assertEntity('main', 'Main menu', 'The Main menu is used on many sites to show the major sections of the site, often in a top navigation bar.'); - $this->assertEntity('admin', 'Management', 'The Management menu contains links for administrative tasks.'); - $this->assertEntity('menu-test-menu', 'Test Menu', 'Test menu description.'); - $this->assertEntity('tools', 'Navigation', 'The Navigation menu contains links intended for site visitors. Links are added to the Navigation menu automatically by some modules.'); - $this->assertEntity('account', 'User menu', 'The User menu contains links related to the user\'s account, as well as the \'Log out\' link.'); + $this->assertEntity('main', 'und', 'Main menu', 'The Main menu is used on many sites to show the major sections of the site, often in a top navigation bar.'); + $this->assertEntity('admin', 'und', 'Management', 'The Management menu contains links for administrative tasks.'); + $this->assertEntity('menu-test-menu', 'und', 'Test Menu', 'Test menu description.'); + $this->assertEntity('tools', 'und', 'Navigation', 'The Navigation menu contains links intended for site visitors. Links are added to the Navigation menu automatically by some modules.'); + $this->assertEntity('account', 'und', 'User menu', 'The User menu contains links related to the user\'s account, as well as the \'Log out\' link.'); + $this->assertEntity('menu-fixedlang', 'is', 'FixedLang', ''); // Test that we can re-import using the ConfigEntityBase destination. Database::getConnection('default', 'migrate') diff --git a/core/modules/system/tests/src/Kernel/Migrate/d7/MigrateMenuTranslationTest.php b/core/modules/system/tests/src/Kernel/Migrate/d7/MigrateMenuTranslationTest.php new file mode 100644 index 0000000000..165c0a579e --- /dev/null +++ b/core/modules/system/tests/src/Kernel/Migrate/d7/MigrateMenuTranslationTest.php @@ -0,0 +1,67 @@ +installSchema('locale', + ['locales_source', 'locales_target', 'locales_location']); + $this->executeMigrations([ + 'language', + 'd7_menu', + 'd7_menu_translation', + ]); + } + + /** + * Tests migration of menu translations. + */ + public function testMenuTranslation() { + $language_manager = \Drupal::service('language_manager'); + + $config_translation = $language_manager->getLanguageConfigOverride('is', 'system.menu.main'); + $this->assertSame('is - Main menu', $config_translation->get('label')); + $this->assertSame('is - Main menu description', $config_translation->get('description')); + + $config_translation = $language_manager->getLanguageConfigOverride('fr', 'system.menu.main'); + $this->assertSame('fr - Main menu', $config_translation->get('label')); + $this->assertSame('fr - Main menu description', $config_translation->get('description')); + + // Translate and localize menu. + $config_translation = $language_manager->getLanguageConfigOverride('fr', 'system.menu.menu-test-menu'); + $this->assertSame('fr - Test menu description', $config_translation->get('description')); + + // No translations for fixed language menu. + $config_translation = $language_manager->getLanguageConfigOverride('fr', 'menu-fixedlang'); + $this->assertNull($config_translation->get('description')); + $this->assertNull($config_translation->get('label')); + $config_translation = $language_manager->getLanguageConfigOverride('is', 'menu-fixedlang'); + $this->assertNull($config_translation->get('description')); + $this->assertNull($config_translation->get('label')); + } + +} diff --git a/core/modules/system/tests/src/Kernel/Plugin/migrate/source/MenuTest.php b/core/modules/system/tests/src/Kernel/Plugin/migrate/source/MenuTest.php index 3917b37cc0..0dc4082df7 100644 --- a/core/modules/system/tests/src/Kernel/Plugin/migrate/source/MenuTest.php +++ b/core/modules/system/tests/src/Kernel/Plugin/migrate/source/MenuTest.php @@ -41,6 +41,19 @@ public function providerSource() { // The expected results are identical to the source data. $tests[0]['expected_data'] = $tests[0]['source_data']['menu_custom']; + $tests[1] = $tests[0]; + $tests[1]['source_data']['menu_custom'][0] += + [ + 'language' => 'it', + 'i18n_mode' => 1, + ]; + $tests[1]['source_data']['menu_custom'][1] += + [ + 'language' => 'fr', + 'i18n_mode' => 2, + ]; + $tests[1]['expected_data'] = $tests[1]['source_data']['menu_custom']; + return $tests; } diff --git a/core/modules/system/tests/src/Kernel/Plugin/migrate/source/d7/MenuTranslationTest.php b/core/modules/system/tests/src/Kernel/Plugin/migrate/source/d7/MenuTranslationTest.php new file mode 100644 index 0000000000..bb7ee54046 --- /dev/null +++ b/core/modules/system/tests/src/Kernel/Plugin/migrate/source/d7/MenuTranslationTest.php @@ -0,0 +1,102 @@ + 'navigation', + 'title' => 'Navigation', + 'description' => 'Navigation description', + 'language' => 'und', + 'i18n_mode' => 0, + ], + [ + 'menu_name' => 'menu-name-2', + 'title' => 'menu custom value 2', + 'description' => 'menu custom description value 2', + 'language' => 'und', + 'i18n_mode' => 0, + ], + ]; + $tests[0]['source_data']['i18n_string'] = [ + [ + 'lid' => 1, + 'textgroup' => 'menu', + 'context' => ' menu:navigation:description', + 'objectid' => 'navigation', + 'type' => 'menu', + 'property' => 'description', + 'objectindex' => 0, + 'format' => '', + ], + [ + 'lid' => 2, + 'textgroup' => 'menu', + 'context' => ' menu:navigation:title', + 'objectid' => 'navigation', + 'type' => 'menu', + 'property' => 'title', + 'objectindex' => 0, + 'format' => '', + ], + ]; + $tests[0]['source_data']['locales_target'] = [ + [ + 'lid' => 1, + 'translation' => 'navigation description translation', + 'language' => 'fr', + 'plid' => 0, + 'plural' => 0, + 'i18n_status' => 0, + ], + [ + 'lid' => 2, + 'translation' => 'navigation translation', + 'language' => 'fr', + 'plid' => 0, + 'plural' => 0, + 'i18n_status' => 0, + ], + ]; + $tests[0]['expected_results'] = [ + [ + 'menu_name' => 'navigation', + 'type' => 'menu', + 'property' => 'description', + 'translation' => 'navigation description translation', + 'language' => 'fr', + 'objectid' => 'navigation', + ], + [ + 'menu_name' => 'navigation', + 'type' => 'menu', + 'property' => 'title', + 'translation' => 'navigation translation', + 'language' => 'fr', + 'objectid' => 'navigation', + ], + ]; + return $tests; + } + +}