-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
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
I18n for static strings in UI #403
Conversation
Hey @vencax, thanks for this! Discussion of 18n for the cms has increased as of late, so it's nice to see some action :) I have two thoughts on direction here:
An implementation along those lines would get us pretty close to something we can merge. Let me know what you think! |
Yes, why not, but I am not sure if I will find time to do in in next week, since I am going to travel bit. Let's see :) |
Ok, I would use polyglot ... |
Polyglot is certainly simpler, agreed. |
Ok, I have reworked the i18n solution with polyglot. Let me know if this is acceptable :) |
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.
Thanks for the contribution! Needs some work, but it's a solid start. The following are some of the particularly important changes:
-
We shouldn't be loading JS dependencies or translation files from third-party URLs at runtime.
polyglot
should be a dependency tracked inpackage.json
and included in thecms.js
bundle. -
polyglot
should not be a window global, and should not be set up in a separate<script>
tag. -
We need to organize and plan out our translation keys so it doesn't get unwieldy as the number of translated strings grows.
example/index.html
Outdated
@@ -6,6 +6,7 @@ | |||
<title>This is an example</title> | |||
|
|||
<link href='https://fonts.googleapis.com/css?family=Roboto:300,400,500' rel='stylesheet' type='text/css'> | |||
<script src="https://rawgit.com/airbnb/polyglot.js/master/build/polyglot.js" type="text/javascript"></script> |
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.
This should be an NPM dependency (https://www.npmjs.com/package/node-polyglot), not a script tag referencing an external resource.
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.
why?
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.
Because this project uses npm for dependency management.
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.
Polyglot is NODE library. There are builds for frontend that ARE loaded as html scripts like I have done. If you install it as npm depend, it will give you error because it rely on node fs module.
Other argument is that not everything should be bundled. For instance React, lodash and some more libs can (and actually should) be externalized to make to bundle size smaller ...
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.
@vencax it isn't just about bundle size, it's about automated dependency management. This project uses npm for that. Webpack should handle importing polyglot just fine.
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 know what you mean, but the version installed by npm is NOT frontend version of polyglot. Try it, and you'll see ...
example/index.html
Outdated
@@ -76,6 +77,9 @@ | |||
</head> | |||
<body> | |||
|
|||
<script> | |||
var polyglot = new Polyglot(); |
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.
This should go in the CMS source, not as a script tag. We also shouldn't be using globals except to provide a hook point for end users (i.e., not for internal CMS stuff).
extract.js
Outdated
marker: 'polyglot.t', | ||
}); | ||
|
||
function _mergeMissing(transls, missing, makeVal) { |
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.
There's no need for the underscore prefix, or for abbreviating translations
as transls
.
extract.js
Outdated
}); | ||
} | ||
|
||
function _writeTransls(file, transls) { |
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.
There's no need for the underscore prefix, or for abbreviating translations
as transls
.
// check english trans first, they MUST be complete !!! | ||
const enFile = 'en.json'; | ||
const enFilePath = path.join(i18nFolder, enFile); | ||
const enTransls = JSON.parse(fs.readFileSync(enFilePath)); |
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.
Use asynchronous IO functions, and be sure to handle errors when doing IO. Don't abbreviate translations
.
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.
the same as above
docs/contributor-guide.md
Outdated
|
||
## i18n | ||
|
||
to extract/update i18n string run: |
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.
This should be expanded, it's quite unclear. It should also be correctly capitalized.
@@ -0,0 +1,59 @@ | |||
const i18n = require('i18n-extract'); |
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.
This file should be moved to scripts
, set up to use webpack (similarly to scripts/autoconfigure.collection.js
), and these require
calls should be changed to import ... from...
.
|
||
const errors = [] | ||
|
||
fs.readdirSync(i18nFolder).forEach(file => { |
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.
Use asynchronous IO functions, and be sure to handle errors when doing IO.
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.
the same as above
extract.js
Outdated
return | ||
} | ||
const filePath = path.join(i18nFolder, file); | ||
const transls = JSON.parse(fs.readFileSync(filePath)); |
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.
Don't abbreviate translations
. Use asynchronous IO actions, and be sure to handle errors when doing IO. (this one is particularly important - as written, the loop will wait for each file to be read in turn, whereas the equivalent asynchronous code could kick off all the file read promises in parallel).
extract.js
Outdated
|
||
function _writeTransls(file, transls) { | ||
const sorted = _.keyArrange(transls) | ||
fs.writeFileSync(file, JSON.stringify(sorted, {}, 2)); |
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.
Use asynchronous IO actions, and be sure to handle errors when doing IO. Take a look at util.promisify
(we may need a polyfill for older Node versions), which lets you use a promise-based interface to the Node stdlib.
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.
it works without async IO, so no need to waste time with syntactic sugar, sorry :)
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'm not talking about asynchronous/await syntax sugar, I'm talking about using fs.writeFile
instead of fs.writeFileSync
.
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.
some of the requested changes solved. Others left with comment below. And the unrelated newlines, really showstopper?
src/actions/config.js
Outdated
@@ -82,6 +83,27 @@ export function configDidLoad(config) { | |||
}; | |||
} | |||
|
|||
const DEFAULT_I18N_URL = 'https://raw.githubusercontent.com/netlify/netlify-cms/master/src'; |
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.
As I mentioned in another comment, we can allow translation objects to be loaded in through the registry and forego any fetching while allowing translation to be independent of cms releases. I believe that would address concerns on both sides, let me know if you think otherwise.
The registry methods are added to the window
object, source is here: https://github.com/netlify/netlify-cms/blob/1d08f1a33b9562e60145dc9704eb6379f64df044/src/lib/registry.js
@vencax the unrelated whitespace changes should be fixed in order to keep the history clean and usable. If you'd prefer l can edit them out and push the commit to the PR. |
@vencax just to reiterate, awesome work on this - we're looking forward to getting it merged. Let me know what you think about using the registry for i18n string objects. @Benaiah can you go ahead and push a commit for removing whitespace changes if there are too many? Also, let me know if you're up for reviewing the naming scheme and providing some standardization so it matches with the general nomenclature elsewhere in the repo. |
I still don't see advantage of using registry. The i18n strings are JSONs so they need to be loaded by request (ok, they can be loaded by webpack json-loader but it is IMHO much much much easier and removes the dependency on rebundlling to use the request). So the question is where to perform the request and the actual language init. NOT how to do it. Or am I missing something else? |
@vencax here's a simplistic breakdown of what I'd expect the Polyglot implementation to look like: In the CMS source:
In the CMS integration page (the user's index.html):
I believe this simplifies things a great deal. Loading json really isn't an issue, just a string to parse. Thoughts? |
Ok, |
Ok, I have used webpack json-loader for load default (english strings). And during config phase if there is translations_url present in config, request is sent to retrieve them and if it is successful polyglot transls are replaced. |
@vencax can you join us on Gitter to discuss? https://gitter.im/netlify/netlifycms It's just occurring to me that this setup requires a build step on the developer's side, and I don't believe that's necessary - we can probably cut out static analysis entirely and provide a simpler integration. A developer should only need to indicate a language in the config to use this, and those wanting something more advance can set it up as they see fit. The other decision to make here is where strings are stored. I was assuming based on your earlier statements that you wanted them outside the CMS, which could work, but I now see that you're storing them within the CMS. If we store them within the CMS, then updating translations will be dependent on releases. I think this is fine since we can release such changes pretty rapidly. If we want to allow folks to override the built in translations, that can happen through the registry, which is the single point of extension for all aspects of the CMS. |
@vencax just took another look - it seems the user has to load Polyglot in their |
NPM version used (I have originally used a wrong lib named the same :/ ). |
Remaining work to get this across the finish line:
|
Deploy preview ready! Built with commit 77bb466 |
Deploy preview ready! Built with commit 77bb466 |
Hi, I have created another PR (#974) so this could be closed. |
Hi, because not all of our editors and contributors speak english I decided to make an i18n for netlify-cms.
The idea is simple: all strings direct through __ function which takes i18n strings object and simply pass given string through that object.
I have included some tools to extract the strings automatically for easier i18n of whole app.
I am looking forward for some feedback alredy :)