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

Fix conflicting strings issue in translations #917

Merged
merged 5 commits into from
Aug 28, 2018

Conversation

notlmn
Copy link
Contributor

@notlmn notlmn commented Aug 26, 2018

Fixes #916
Fixes #324

Motivation

Initially experienced at babel/website#1770 (comment), later worked along with @endiliey on Discord room to find the issues and work on a fix. The main issues are explained in #916.

Have you read the Contributing Guidelines on pull requests?

Yes I did

Test Plan

Tested on sample repo, to work as expected as explained in #916

Related PRs

This PR changes the way customized localization strings are handled. Existing setups should not break that bad, but the new translations are to be used as explained in the docs.

Copy link
Contributor

@JoelMarcey JoelMarcey left a comment

Choose a reason for hiding this comment

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

@notlmn Thank you so much for fixing this. ❤️

This seems reasonable to me.

You mentioned that you worked with @endiliey on Discord, so I want to make sure this captures your conversation before we merge.

@docusaurus-bot
Copy link
Contributor

docusaurus-bot commented Aug 27, 2018

Deploy preview for docusaurus-preview ready!

Built with commit cf1405c

https://deploy-preview-917--docusaurus-preview.netlify.com

Copy link
Contributor

@endiliey endiliey left a comment

Choose a reason for hiding this comment

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

Looks great. This might impact users since the translation strings data structure we create & send to crowdin will be slightly different, but that's okay 😄

However, please see my review below. Just some minor questions & changes needed.

translations['localized-strings'] = Object.assign(
translations['localized-strings'],
customTranslations['localized-strings']
translations['localized-strings'].links = Object.assign(
Copy link
Contributor

@endiliey endiliey Aug 27, 2018

Choose a reason for hiding this comment

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

Any reason not to simply deepmerge ?

IMHO using Object assign for one level deep and deepmerge for > one level deep is not as declarative as simply deepmerging

translations['localized-strings'] = deepmerge(
  translations['localized-strings'],
  customTranslations['localized-strings']
);

instead of
https://github.com/facebook/Docusaurus/blob/dae9e442a4eb4ed0f8b69254d0f7ae9093dfd34f/lib/write-translations.js#L186-L201

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Missed that one!

this.props.metadata.localized_id
] || this.props.metadata.title
: this.props.metadata.title;
? (i18n['localized-strings'].docs[id] &&
Copy link
Contributor

@endiliey endiliey Aug 27, 2018

Choose a reason for hiding this comment

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

Seeing this made me realize that we should create a safe getter to access deeply nested object.
We don't want to keep doing this. Example:

// access deeply nested values...
translation &&
translation[this.props.metadata.language] &&
translation[this.props.metadata.language].docs  &&
translation[this.props.metadata.language].docs[id]

We can create a small helper function (maybe in core/utils.js)

const idx = (target, path) =>
  path.reduce((obj, key) => (obj && obj[key] ? obj[key] : null), target);

So we can write

const title = idx(i18n, ['localized-strings', 'docs', id, 'title']) || defaultTitle;

instead of

const title = i18n ? (i18n['localized-strings'].docs[id] && i18n['localized-strings'].docs[id].title) || defaultTitle : defaultTitle;

WDYT ?

Copy link
Contributor Author

@notlmn notlmn Aug 27, 2018

Choose a reason for hiding this comment

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

I used almost the same method you used to access a deep property, but modified it so that the function accepts the path as a string rather than an array, because most of the libraries out there follow the same pattern.

If we need to be more safe then we can move to any library like just-safe-get.

@endiliey endiliey requested a review from yangshun August 27, 2018 10:24
] || this.props.metadata.title
: this.props.metadata.title;
const title =
utils.getDeepProp(i18n, `localized-strings.docs.${id}.title`) ||
Copy link
Contributor

Choose a reason for hiding this comment

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

Note that id can be in the form of string with dot inside. Example: version-1.0.0-hello-world

If we are splitting by ., this could cause a bug

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch, will fix it!

Copy link
Contributor

@endiliey endiliey Aug 28, 2018

Choose a reason for hiding this comment

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

I am still in favor of using array. It is more declarative to me. The dot notation will only work if there is no key that contains a dot.

This is why we have to do props['hello.world'] sometimes because props.hello.world wont work

Compare
['localized-string', id] vs 'localized-string.${id}'

In fact just-safe-get library you mentioned also support accessing deep nested pros using array path

import get from 'just-safe-get';
 
const obj = {a: {aa: {aaa: 2}}, b: 4};
 
get(obj, 'a.aa.aaa'); // 2
get(obj, ['a', 'aa', 'aaa']); // 2

Copy link
Contributor

@endiliey endiliey Aug 28, 2018

Choose a reason for hiding this comment

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

But we dont need a library for that. The idx function implementation I mentioned in last review should be quite ok.

The naming of idx is because Facebook internally have that function.

https://facebook.github.io/react-native/blog/2017/03/13/idx-the-existential-function

But it requires babel plugin 😂
https://github.com/facebookincubator/idx

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I fixed and renamed it back to idx!

Edited it a little bit to return actual value instead of returning null on every falsy value.

Copy link
Contributor

@endiliey endiliey left a comment

Choose a reason for hiding this comment

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

Looks great. It's time to ship

shipit

@endiliey endiliey merged commit cfabaed into facebook:master Aug 28, 2018
@endiliey endiliey removed the request for review from yangshun August 28, 2018 16:17
@notlmn notlmn deleted the fix-localized-strings-conflict branch August 28, 2018 17:23
@notlmn
Copy link
Contributor Author

notlmn commented Aug 28, 2018

@endiliey Can you do a new release real quick?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed Signed Facebook CLA
Projects
None yet
5 participants