-
Notifications
You must be signed in to change notification settings - Fork 231
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Blueprints: Add setSiteLanguage step to change the language #1538
Conversation
@adamziel do you know why the documentation build is failing? I tried debugging it locally but didn't get far. |
packages/playground/blueprints/src/lib/steps/set-site-language.ts
Outdated
Show resolved
Hide resolved
I'd like to add my https://github.com/akirk/playground-step-library/blob/main/steps/setLanguage.js implementation to the discussion here. This is quite imperfect as it should use language packs (see https://api.wordpress.org/translations/core/1.0/ and https://api.wordpress.org/translations/plugins/1.0/?slug=friends) so that we get all the JSON files as well. But note how we also need to take into account plugins and themes that are installed, we need to get their translations, too. |
Thank you @akirk this is amazing! |
I am not sure I should, I don't know the code base well enough and will be off next week and I don't want to stall this. I just tried to say that this is possible with the step library but that you also need to iterate the plugins to be installed, something that I think you had not addressed yet. |
@adamziel, @bgrgicak, and @akirk, I think this is a good example of a place that calls for separate config languages/concepts (DSLs, not human language translations):
IMO, human language selection is part of platform truth -- "The current language is X" -- as is FS construction of various mount configurations. Things like mounts can have an order of application, but they are more like platform declarations than actions. They are foundational. They are the basis upon which everything else is built. Actions are things done upon the established foundation or platform at runtime: Install this, write that, login, etc. There is a bit of gray area in this conceptualization of Platform vs Runtime, because each runtime action builds upon the version of reality established by the previous action. But I think we can also separate the two concepts by asking:
Both language and directory mounts are relevant to the Playground boot process. We want to establish the filesystem before booting, and as part of booting Wordpress, we could download the language for WordPress itself. Then, at runtime, the installPlugin step could recognize the current language and download the right translation as part of its install work. |
Instead of saying "separate DSLs", we could instead say things like:
I also meant to ask: |
Related to the platform vs actions idea, one question is: |
If we want to translate plugin and themes, it should come at the end. |
Sorry I'm focused on wrapping up offline support and will get back to this next week. |
I agree, but steps are the only thing we have today. We discussed in the past that there could be a way to separate boot actions from runtime actions in the blueprint. These look like good examples of it. |
a719571
to
8cc084f
Compare
I moved the code to compile and implemented downloads in JS instead of PHP. I had to use @akirk @pkevan Do you know of any methods that would allow us to download the latest translations of a plugin/theme without knowing the version number? Theme/plugin translation code diff
|
Not exactly sure what you are trying to do here, but you can use: https://api.wordpress.org/plugins/info/1.0/wordpress-beta-tester.json to grab all versions, then pick the appropriate one. Unsure if there are other methods available, or if it's worth building a specific endpoint on api.wordpress.org. |
We want to add translation support to Playground.
Thanks! I want to avoid that because it adds one extra request for each plugin and theme we want to install. |
Adding CORS support is a request via systems, see: https://make.wordpress.org/systems/?s=cors |
@akirk I explored this approach, but it complicates things a lot if we use blueprint steps to get translations. When it comes to plugin and theme translations it's even more complicated because we need plugin version data and this adds extra requests. |
There is a function |
That's what I'm using, but it looks like it downloads only core translations. |
For reference, I cooked up this little piece of PHP code to install a language pack: function install_language_pack( $type, $slug, $lang ) {
if ( ! in_array( $type, [ 'plugin', 'theme' ] ) ) {
return new WP_Error( 'invalid_type', __( 'Invalid type' ) );
}
require_once ABSPATH . 'wp-admin/includes/translation-install.php';
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
$language_updates = translations_api( $type . 's', compact( 'slug' ) );
if ( is_wp_error( $language_updates ) ) {
return $language_updates;
}
$packages = array_filter( $language_updates['translations'], fn( $pkg ) => $pkg['language'] === $lang );
if ( empty( $packages ) ) {
return;
}
$upgrader = new Language_Pack_Upgrader( new Automatic_Upgrader_Skin() );
$update = (object) $packages[0];
$update->type = $type;
$update->slug = $slug;
return $upgrader->bulk_upgrade( array( $update ) );
} For example: install_language_pack( 'plugin', 'friends', 'de_DE' );
=> array(1) {
[0]=>
array(7) {
["source"]=>
string(70) "/Users/alex/Sites/localhost/wp/wp-content/upgrade/friends-2.9.3-de_de/"
["source_files"]=>
array(7) {
[0]=>
string(22) "friends-de_DE.l10n.php"
[1]=>
string(51) "friends-de_DE-7ffd8fc6ef503cd5a5c483bb3aca9bc5.json"
[2]=>
string(51) "friends-de_DE-93774191f1ed3f0224434004c216505a.json"
[3]=>
string(16) "friends-de_DE.mo"
[4]=>
string(51) "friends-de_DE-9a6fba47b355b62e8f6712f5c40b6192.json"
[5]=>
string(51) "friends-de_DE-85be8386efdca7f7b168c9c67defc614.json"
[6]=>
string(16) "friends-de_DE.po"
}
["destination"]=>
string(59) "/Users/alex/Sites/localhost/wp/wp-content/languages/plugins"
["destination_name"]=>
string(0) ""
["local_destination"]=>
string(59) "/Users/alex/Sites/localhost/wp/wp-content/languages/plugins"
["remote_destination"]=>
string(60) "/Users/alex/Sites/localhost/wp/wp-content/languages/plugins/"
["clear_destination"]=>
bool(true)
}
} |
Thanks @akirk! Let's keep the JS implementation for now, but this will be useful when we get to implementing PHP blueprints. |
LGTM, I just have one note. When I use an invalid language code, like http://localhost:5400/website-server/?plugin=friends&theme=twentytwentythree&language=pl |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I cannot say too much about whether the code approach is right (I see one unrelated change that Adam already flagged) but from a language pack perspective it looks good and works nicely.
I am happy to see that actually an old behavior on translate.wordpress.org has been improved: language packs will always be built for the newest version so that constructing the download URL using the plugin version number just works.
It used to be the case that you really needed that API call to identify the plugin version for which the last language pack was built. (for example, if it was translated to 100% in Spanish in version 1.0 and then because of a huge string update the translation ratio changed to 50% in version 2.0, the language pack would remain at 1.0, nowadays it looks like it is also build for 2.0).
/** | ||
* If a core translation wasn't found we should throw an error because it means the language is not supported or the language code isn't correct. | ||
*/ | ||
if (type === 'core') { | ||
throw new Error( | ||
`Failed to download translations for WordPress. Please check if the language code ${language} is correct. You can find all available languages and translations on https://translate.wordpress.org/.` | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I couldn't find a way in WP to check if the language code is valid, so I'm relying on API errors.
If a translation for WordPress core doesn't exist, the step will fail.
Theme and plugin translations are still allowed to fail because they have much lower translation rates.
|
||
for (const { url, type } of translations) { | ||
try { | ||
const response = await fetch(url); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One last nit: Let's fetch using a promise queue instead of doing it sequentially
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, let's merge as is and do the promise queue in the PHP implementation.
I added a usage example to the PR description and updated this Blueprint to use the new step: https://github.com/WordPress/blueprints/blob/trunk/blueprints/translations/blueprint.json |
Motivation for the change, related issues
To unlock Playground to non-English speakers we want to make it easier for sites to be loaded in a local language.
Users can easily switch the site language by adding a
language=LANGUAGE_CODE
query parameter.Implementation details
This PR adds a
language
parameter to the Query API which adds asetSiteLanguage
blueprint step.We could have achieved this without a new step, but it would require adding two steps to the blueprint generator (add constant, run PHP).
The step downloads core, plugin, and theme translations from Download.WordPress.org and moves the files into the translation directory.
Step usage example:
Testing Instructions (or ideally a Blueprint)