Skip to content

Commit a43e354

Browse files
committed
Upgrade/Install: List available translation updates on Updates screen.
1 parent 047ef80 commit a43e354

File tree

3 files changed

+355
-5
lines changed

3 files changed

+355
-5
lines changed

src/wp-admin/includes/update.php

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,136 @@ function get_theme_updates() {
643643
return $update_themes;
644644
}
645645

646+
/**
647+
* Gets the display name for a translation update.
648+
*
649+
* @since 6.7.0
650+
*
651+
* @param object $update Translation update object.
652+
* @return string The translation update name.
653+
*/
654+
function wp_get_translation_update_name( $update ) {
655+
$type = isset( $update->type ) ? $update->type : '';
656+
$slug = isset( $update->slug ) ? $update->slug : '';
657+
658+
switch ( $type ) {
659+
case 'core':
660+
return 'WordPress'; // Not translated.
661+
662+
case 'theme':
663+
$theme = wp_get_theme( $slug );
664+
if ( $theme->exists() ) {
665+
return $theme->get( 'Name' );
666+
}
667+
break;
668+
669+
case 'plugin':
670+
require_once ABSPATH . 'wp-admin/includes/plugin.php';
671+
672+
$plugin_names = array();
673+
$all_plugins = get_plugins();
674+
$plugin_update = get_site_transient( 'update_plugins' );
675+
676+
if ( is_object( $plugin_update ) ) {
677+
$plugin_updates = array();
678+
679+
if ( ! empty( $plugin_update->response ) && is_array( $plugin_update->response ) ) {
680+
$plugin_updates = array_merge( $plugin_updates, $plugin_update->response );
681+
}
682+
683+
if ( ! empty( $plugin_update->no_update ) && is_array( $plugin_update->no_update ) ) {
684+
$plugin_updates = array_merge( $plugin_updates, $plugin_update->no_update );
685+
}
686+
687+
foreach ( $plugin_updates as $plugin_file => $plugin_data ) {
688+
$plugin_data = (object) $plugin_data;
689+
690+
if ( ! empty( $plugin_data->slug ) && ! empty( $all_plugins[ $plugin_file ]['Name'] ) ) {
691+
$plugin_names[ $plugin_data->slug ] = $all_plugins[ $plugin_file ]['Name'];
692+
}
693+
}
694+
}
695+
696+
foreach ( $all_plugins as $plugin_file => $plugin_data ) {
697+
$plugin_basename = dirname( $plugin_file );
698+
699+
if ( isset( $plugin_data['Name'] ) ) {
700+
$plugin_names[ $plugin_file ] = $plugin_data['Name'];
701+
$plugin_names[ basename( $plugin_file, '.php' ) ] = $plugin_data['Name'];
702+
703+
if ( '.' !== $plugin_basename ) {
704+
$plugin_names[ $plugin_basename ] = $plugin_data['Name'];
705+
}
706+
}
707+
}
708+
709+
if ( ! empty( $plugin_names[ $slug ] ) ) {
710+
return $plugin_names[ $slug ];
711+
}
712+
break;
713+
}
714+
715+
return $slug;
716+
}
717+
718+
/**
719+
* Gets the display language for a translation update.
720+
*
721+
* @since 6.7.0
722+
*
723+
* @param string $locale Translation update locale.
724+
* @return string The translation update language.
725+
*/
726+
function wp_get_translation_update_language( $locale ) {
727+
$translations = get_site_transient( 'available_translations' );
728+
729+
if ( is_array( $translations ) && ! empty( $translations[ $locale ]['native_name'] ) ) {
730+
return sprintf(
731+
/* translators: 1: Native language name, 2: Locale. */
732+
__( '%1$s (%2$s)' ),
733+
$translations[ $locale ]['native_name'],
734+
$locale
735+
);
736+
}
737+
738+
return $locale;
739+
}
740+
741+
/**
742+
* Gets display data for available translation updates.
743+
*
744+
* @since 6.7.0
745+
*
746+
* @return array[] {
747+
* An array of translation update display data.
748+
*
749+
* @type string $language Translation update language.
750+
* @type string $language_code Translation update locale.
751+
* @type string $name Translation update name.
752+
* @type string $slug Translation update slug.
753+
* @type string $type Translation update type.
754+
* @type string $version Translation update version.
755+
* }
756+
*/
757+
function wp_get_translation_update_data() {
758+
$translation_updates = array();
759+
760+
foreach ( wp_get_translation_updates() as $update ) {
761+
$language = isset( $update->language ) ? $update->language : '';
762+
763+
$translation_updates[] = array(
764+
'language' => wp_get_translation_update_language( $language ),
765+
'language_code' => $language,
766+
'name' => wp_get_translation_update_name( $update ),
767+
'slug' => isset( $update->slug ) ? $update->slug : '',
768+
'type' => isset( $update->type ) ? $update->type : '',
769+
'version' => isset( $update->version ) ? $update->version : '',
770+
);
771+
}
772+
773+
return $translation_updates;
774+
}
775+
646776
/**
647777
* Adds a callback to display update information for themes with updates available.
648778
*

src/wp-admin/update-core.php

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -813,20 +813,44 @@ function list_theme_updates() {
813813
* @since 3.7.0
814814
*/
815815
function list_translation_updates() {
816-
$updates = wp_get_translation_updates();
817-
if ( ! $updates ) {
816+
$translation_updates = wp_get_translation_update_data();
817+
if ( ! $translation_updates ) {
818818
if ( 'en_US' !== get_locale() ) {
819819
echo '<h2>' . __( 'Translations' ) . '</h2>';
820820
echo '<p>' . __( 'Your translations are all up to date.' ) . '</p>';
821821
}
822822
return;
823823
}
824824

825-
$form_action = 'update-core.php?action=do-translation-upgrade';
825+
$form_action = 'update-core.php?action=do-translation-upgrade';
826+
$updates_count = count( $translation_updates );
826827
?>
827-
<h2><?php _e( 'Translations' ); ?></h2>
828+
<h2>
829+
<?php
830+
printf(
831+
'%s <span class="count">(%d)</span>',
832+
__( 'Translations' ),
833+
number_format_i18n( $updates_count )
834+
);
835+
?>
836+
</h2>
828837
<form method="post" action="<?php echo esc_url( $form_action ); ?>" name="upgrade-translations" class="upgrade">
829-
<p><?php _e( 'New translations are available.' ); ?></p>
838+
<p><?php _e( 'The following translation updates are available. Update Translations to install them.' ); ?></p>
839+
<ul class="ul-disc">
840+
<?php foreach ( $translation_updates as $translation_update ) : ?>
841+
<li>
842+
<strong><?php echo esc_html( $translation_update['name'] ); ?></strong>:
843+
<?php
844+
printf(
845+
/* translators: 1: Language name or locale, 2: Version number. */
846+
__( '%1$s translation is available for version %2$s.' ),
847+
esc_html( $translation_update['language'] ),
848+
esc_html( $translation_update['version'] )
849+
);
850+
?>
851+
</li>
852+
<?php endforeach; ?>
853+
</ul>
830854
<?php wp_nonce_field( 'upgrade-translations' ); ?>
831855
<p><input class="button" type="submit" value="<?php esc_attr_e( 'Update Translations' ); ?>" name="upgrade" /></p>
832856
</form>
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
<?php
2+
3+
/**
4+
* @group admin
5+
* @group upgrade
6+
*
7+
* @covers ::wp_get_translation_update_data
8+
* @covers ::wp_get_translation_update_language
9+
* @covers ::wp_get_translation_update_name
10+
*/
11+
class Tests_Admin_IncludesUpdate extends WP_UnitTestCase {
12+
/**
13+
* Sets up shared fixtures.
14+
*/
15+
public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
16+
require_once ABSPATH . 'wp-admin/includes/update.php';
17+
}
18+
19+
public function tear_down() {
20+
delete_site_transient( 'available_translations' );
21+
delete_site_transient( 'update_core' );
22+
delete_site_transient( 'update_plugins' );
23+
delete_site_transient( 'update_themes' );
24+
remove_all_filters( 'all_plugins' );
25+
26+
parent::tear_down();
27+
}
28+
29+
/**
30+
* @ticket 42281
31+
*/
32+
public function test_wp_get_translation_update_data_returns_display_ready_translation_updates() {
33+
set_site_transient(
34+
'available_translations',
35+
array(
36+
'de_DE' => array(
37+
'native_name' => 'Deutsch',
38+
),
39+
)
40+
);
41+
42+
set_site_transient(
43+
'update_core',
44+
(object) array(
45+
'translations' => array(
46+
array(
47+
'type' => 'core',
48+
'slug' => 'default',
49+
'language' => 'de_DE',
50+
'version' => '6.7-beta3',
51+
),
52+
),
53+
)
54+
);
55+
56+
set_site_transient(
57+
'update_plugins',
58+
(object) array(
59+
'translations' => array(
60+
array(
61+
'type' => 'plugin',
62+
'slug' => 'custom-internationalized-plugin',
63+
'language' => 'de_DE',
64+
'version' => '1.0.0',
65+
),
66+
),
67+
)
68+
);
69+
70+
set_site_transient(
71+
'update_themes',
72+
(object) array(
73+
'translations' => array(
74+
array(
75+
'type' => 'theme',
76+
'slug' => 'custom-internationalized-theme',
77+
'language' => 'de_DE',
78+
'version' => '1.0.0',
79+
),
80+
),
81+
)
82+
);
83+
84+
$this->assertSame(
85+
array(
86+
array(
87+
'language' => 'Deutsch (de_DE)',
88+
'language_code' => 'de_DE',
89+
'name' => 'WordPress',
90+
'slug' => 'default',
91+
'type' => 'core',
92+
'version' => '6.7-beta3',
93+
),
94+
array(
95+
'language' => 'Deutsch (de_DE)',
96+
'language_code' => 'de_DE',
97+
'name' => 'Custom Dummy Plugin',
98+
'slug' => 'custom-internationalized-plugin',
99+
'type' => 'plugin',
100+
'version' => '1.0.0',
101+
),
102+
array(
103+
'language' => 'Deutsch (de_DE)',
104+
'language_code' => 'de_DE',
105+
'name' => 'Custom Internationalized Theme',
106+
'slug' => 'custom-internationalized-theme',
107+
'type' => 'theme',
108+
'version' => '1.0.0',
109+
),
110+
),
111+
wp_get_translation_update_data()
112+
);
113+
}
114+
115+
/**
116+
* @ticket 42281
117+
*/
118+
public function test_wp_get_translation_update_data_falls_back_to_locale_and_slug() {
119+
set_site_transient(
120+
'update_plugins',
121+
(object) array(
122+
'translations' => array(
123+
array(
124+
'type' => 'plugin',
125+
'slug' => 'missing-plugin',
126+
'language' => 'it_IT',
127+
'version' => '2.0.0',
128+
),
129+
),
130+
)
131+
);
132+
133+
$this->assertSame(
134+
array(
135+
array(
136+
'language' => 'it_IT',
137+
'language_code' => 'it_IT',
138+
'name' => 'missing-plugin',
139+
'slug' => 'missing-plugin',
140+
'type' => 'plugin',
141+
'version' => '2.0.0',
142+
),
143+
),
144+
wp_get_translation_update_data()
145+
);
146+
}
147+
148+
/**
149+
* @ticket 42281
150+
*/
151+
public function test_wp_get_translation_update_data_matches_plugin_update_slug_to_plugin_file() {
152+
add_filter(
153+
'all_plugins',
154+
static function () {
155+
return array(
156+
'hello.php' => array(
157+
'Name' => 'Hello Dolly',
158+
),
159+
);
160+
}
161+
);
162+
163+
set_site_transient(
164+
'update_plugins',
165+
(object) array(
166+
'translations' => array(
167+
array(
168+
'type' => 'plugin',
169+
'slug' => 'hello-dolly',
170+
'language' => 'de_DE',
171+
'version' => '1.7.2',
172+
),
173+
),
174+
'no_update' => array(
175+
'hello.php' => (object) array(
176+
'slug' => 'hello-dolly',
177+
),
178+
),
179+
)
180+
);
181+
182+
$this->assertSame(
183+
array(
184+
array(
185+
'language' => 'de_DE',
186+
'language_code' => 'de_DE',
187+
'name' => 'Hello Dolly',
188+
'slug' => 'hello-dolly',
189+
'type' => 'plugin',
190+
'version' => '1.7.2',
191+
),
192+
),
193+
wp_get_translation_update_data()
194+
);
195+
}
196+
}

0 commit comments

Comments
 (0)