Skip to content
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

Gutenberg: Load Jetpack block translations in Calypso synchronously #28304

Merged
merged 4 commits into from
Dec 3, 2018

Conversation

tyxla
Copy link
Member

@tyxla tyxla commented Nov 6, 2018

We already have a working i18n system for Jetpack blocks that works well in wp-admin of Jetpack sites. However, currently, we don't serve those translations for Jetpack blocks to Calypso. This means that any of the Jetpack blocks aren't being localized.

This isn't straightforward, because those translations are managed at WP.org, and Calypso's translations are served by WP.com. So what we're already doing on the WP.com side is:

  • Indexing the localized strings in WP.com
  • Fetching and storing all Jetpack block translations from all locales.
  • Serving the translations for Jetpack blocks separately in WP.com

So, currently we'll have the translations available at URLs like: https://widgets.wp.com/languages/jetpack-gutenberg-blocks/bg.json

Next we need to actually pass these to Calypso, so blocks can be translated.

However, it's worth to mention that asynchronously loading those translations has some challenges we don't have solutions for:

  • Block registration occurs early. If it occurs before translations are loaded, we'll end up with untranslated block names, descriptions and everything else that's not rendered live.
  • Blocks can't be registered late. If we try registering them late, Gutenberg might think these blocks don't exist, and will throw some warnings, and we don't want that.

We can register blocks without translations, then after translations are loaded, unregister and register them again with translations, however theoretically this will break those blocks in cases where they're needed while we're doing this re-registration. Even though we'll need to come up with a totally different mechanism for registering blocks, this is an alternative approach that is probably worth experimenting with.

So, this PR suggests that we load the translations synchronously, and we instantiate the editor after that. This way we'll only have to register the blocks once, and at that time they'll actually be translated already. The downside with this approach is that the first time translations are loaded, it might cause a little delay before initializing the editor. Any further initialization should be quick, as the translations file will already be available in the browser cache.

Changes proposed in this Pull Request

  • Synchronously load translations for Jetpack blocks.

Testing instructions

Note

This is currently blocked by #28867.

@tyxla tyxla added Jetpack [Status] Needs Review The PR is ready for review. This also triggers e2e canary tests and wp-desktop tests automatically. i18n [Goal] Gutenberg Working towards full integration with Gutenberg labels Nov 6, 2018
@tyxla tyxla self-assigned this Nov 6, 2018
@matticbot
Copy link
Contributor

@tyxla tyxla requested review from a team November 6, 2018 12:09
@vindl
Copy link
Member

vindl commented Nov 6, 2018

@tyxla just a note that I've recently refactored the way the editor is initialized in order to resolve some bugs, which might have some consequences for your approach here: #28302

@@ -42,6 +48,36 @@ function registerDataPlugins( userId ) {
use( plugins.controls );
}

export const jetpackBlocki18n = ( context, next ) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about calling this function here https://github.com/Automattic/wp-calypso/blob/master/client/gutenberg/editor/init.js#L25 before the extensions are loaded?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, I think I'll pass because:

  • We need access to the state, in order to fetch the user's current locale.
  • For the suggested approach, we need to pause the flow and continue after translations are loaded.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could pass the state (or part of it) to initGutenberg from controller, but I agree that we shouldn't do it here because of your second point.

const languageFileUrl =
'https://widgets.wp.com/languages/jetpack-gutenberg-blocks/' + localeSlug + '.json';

request.get( languageFileUrl ).end( ( error, response ) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor terminology nit, this isn't a synchronous call (as laid out in the title and description of the PR), as it doesn't block the browser UI while it's inflight. That said, we should not block the browser UI by using a true synchronous request, so this is perfectly fine, unless you're expecting it to block the browser...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, good point, this is really sync-ish, because we don't prevent browser from loading anything, but in the same time we won't call next() until the translations request is finished.

@tyxla tyxla force-pushed the try/gutenberg-calypso-jetpack-i18n-take-1 branch from 94fbc60 to 42bf6e5 Compare November 8, 2018 13:49
@tyxla
Copy link
Member Author

tyxla commented Nov 8, 2018

@tyxla just a note that I've recently refactored the way the editor is initialized in order to resolve some bugs, which might have some consequences for your approach here: #28302

Thanks @vindl, I've rebased the PR and it's still working as expected, so it's ready for another review 👀

@ockham
Copy link
Contributor

ockham commented Nov 22, 2018

Rebased.

Copy link
Contributor

@ockham ockham left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, and tests well. While it'd be nice to have some more general-purpose solution in Gutenberg (see WordPress/gutenberg#12232), this is a reasonable solution for now. 🚢

As discussed on a video call with some Luna and Poseidon folks earlier today, we'll actually follow @sirreal's suggestion and use a similar pattern to fetch block visibility data (from @roccotripaldi's endpoint (D21225-code)). /cc @enejb

@tyxla
Copy link
Member Author

tyxla commented Nov 23, 2018

I'd hold off merging for now - I prefer getting approval from @Automattic/i18n and @Automattic/lannister before going ahead with this one.

const languageFileUrl =
'https://widgets.wp.com/languages/jetpack-gutenberg-blocks/' + localeSlug + '.json';

request.get( languageFileUrl ).end( ( error, response ) => {
Copy link
Member

@dmsnell dmsnell Nov 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we shouldn't need to pull in superagent here and instead could consider using the data layer…

import { requestHttpData, waitForData } from 'state/data-layer/http-data';
import { rawHttp } from 'state/data-getters';

waitForData( {
	translations: requestHttpData(
		'jetpack-gutenberg-translations',
		rawHttp( {
			method: 'GET',
			url: `https://widgets.wp.com/languages/jetpack-gutenberg-blocks/${ localeSlug }.json`,
		} ),
		{ fromApi: () => ( { body } ) => [ [ 'jetpack-gutenberg-translations', body ] ] }
	)
} ).then( ( { translations: { state, data } ) => {
	if ( state === 'failure' ) {
		debug( `Encountered an error loading locale file for ${ localeSlug }. Falling back to English.` );
	}

	// could still be expired data in the system
	if ( data ) {
		setLocaleData( data, 'jetpack' );
	}

	next();
} );

granted, that's more code, but it's inspectable in our dev tools and comes with freshness and auto-retry policies and you can fake the data in testing easily enough. if you are interested I'd be happy to create a helper function in state/data-getters which cuts down on the overhead for doing a basic raw HTTP request

waitForData( {
	translations: getAtURL( `https://widgets.wp.com/…` ),
} ).then( ( { translations: { state, data } } ) => {
	if ( 'failure' === state ) {
		debug( '…' );
	}

	if ( data ) {
		setLocaleData( data.body, 'jetpack' );
	}

	next();
} );

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#28867

^^^ in case you are interested…

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is lovely, thanks @dmsnell ❤️

I've implemented your suggestion in this PR, and it's ready for another look.

dmsnell added a commit that referenced this pull request Nov 26, 2018
See #28304

In this patch we're adding a small helper to be used to get raw
information from a given URL though a `GET` request.

The way we'd use this is inside of the `waitForData()` function. This is
meant to obscure the global http-data ID so that the user doesn't need
to worry about it.

```js
waitForData( {
	planets: getAtURL( 'https://swapi.co/api/planets/' ),
} ).then( ( { planets } ) => {
	console.log( planets.data );
} );
```
@tyxla tyxla force-pushed the try/gutenberg-calypso-jetpack-i18n-take-1 branch from 2acd7cf to 373bd5b Compare November 28, 2018 09:32
tyxla pushed a commit that referenced this pull request Nov 28, 2018
See #28304

In this patch we're adding a small helper to be used to get raw
information from a given URL though a `GET` request.

The way we'd use this is inside of the `waitForData()` function. This is
meant to obscure the global http-data ID so that the user doesn't need
to worry about it.

```js
waitForData( {
	planets: getAtURL( 'https://swapi.co/api/planets/' ),
} ).then( ( { planets } ) => {
	console.log( planets.data );
} );
```
dmsnell added a commit that referenced this pull request Nov 28, 2018
See #28304

In this patch we're adding a small helper to be used to get raw
information from a given URL though a `GET` request.

The way we'd use this is inside of the `waitForData()` function. This is
meant to obscure the global http-data ID so that the user doesn't need
to worry about it.

```js
waitForData( {
	planets: getAtURL( 'https://swapi.co/api/planets/' ),
} ).then( ( { planets } ) => {
	console.log( planets.data );
} );
```
@tyxla
Copy link
Member Author

tyxla commented Nov 29, 2018

This is currently blocked by #28867.

dmsnell added a commit that referenced this pull request Nov 29, 2018
See #28304

In this patch we're adding a small helper to be used to get raw
information from a given URL though a `GET` request.

The way we'd use this is inside of the `waitForData()` function. This is
meant to obscure the global http-data ID so that the user doesn't need
to worry about it.

```js
waitForData( {
	planets: getAtURL( 'https://swapi.co/api/planets/' ),
} ).then( ( { planets } ) => {
	console.log( planets.data );
} );
```
@ockham ockham added the [Pri] High Address as soon as possible after BLOCKER issues label Nov 30, 2018
ockham pushed a commit that referenced this pull request Dec 3, 2018
See #28304

In this patch we're adding a small helper to be used to get raw
information from a given URL though a `GET` request.

The way we'd use this is inside of the `waitForData()` function. This is
meant to obscure the global http-data ID so that the user doesn't need
to worry about it.

```js
waitForData( {
	planets: requestFromUrl( 'https://swapi.co/api/planets/' ),
} ).then( ( { planets } ) => {
	console.log( planets.data );
} );
```
@ockham ockham force-pushed the try/gutenberg-calypso-jetpack-i18n-take-1 branch from 373bd5b to 685a69b Compare December 3, 2018 10:38
@ockham
Copy link
Contributor

ockham commented Dec 3, 2018

Rebased

const localeSlug = getCurrentLocaleSlug( state );

// We don't need to localize English
if ( localeSlug === 'en' ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to use config( 'i18n_default_locale_slug' ) here

@ockham
Copy link
Contributor

ockham commented Dec 3, 2018

Merging. Thanks Marin, Dennis, et al

@ockham ockham merged commit 78430d0 into master Dec 3, 2018
@ockham ockham deleted the try/gutenberg-calypso-jetpack-i18n-take-1 branch December 3, 2018 11:14
@matticbot matticbot removed the [Status] Needs Review The PR is ready for review. This also triggers e2e canary tests and wp-desktop tests automatically. label Dec 3, 2018
@dmsnell
Copy link
Member

dmsnell commented Dec 4, 2018

Note that this is affected by the bug fixed in #29043

@tyxla
Copy link
Member Author

tyxla commented Dec 5, 2018

Thanks for landing this one @ockham! 🙇

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Goal] Gutenberg Working towards full integration with Gutenberg i18n Jetpack [Pri] High Address as soon as possible after BLOCKER issues [Status] Blocked / Hold
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants