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

React/Javascript translation customisations are difficult #1325

Open
1 task
jonom opened this issue Jul 18, 2019 · 12 comments
Open
1 task

React/Javascript translation customisations are difficult #1325

jonom opened this issue Jul 18, 2019 · 12 comments

Comments

@jonom
Copy link

jonom commented Jul 18, 2019

Affected Version

SS 4.x

Description

Opening this issue at @robbieaverill's suggestion. From the docs:

it is pretty common for a site developer to want to override the default i18n strings from time to time

In SS3 if you wanted to override a piece of text for any part of the CMS UI, you could generally just add a language yml file to your project and override the relevant key. Likewise if you wanted to translate the CMS to a new language (or add missing keys for an existing language) and see your translations in the CMS before they were accepted in Transifex and shipped in a release. Now that parts of the CMS are built in React that seems to be a lot harder (example awful hack).

Besides being inconvenient, I think it could be confusing for developers if yml lang files work for some parts of the CMS but not others as it's inconsistent (and I don't think the limitation is documented as far as I can tell). I'm not sure if this is possible, but it would be nice if translation json files for React components were built on the fly for each project in the flush step, so that additional and/or custom translations were accounted for.

Pull Request

@robbieaverill
Copy link
Contributor

Yeah we definitely need to build a JavaScript text collector

@michalkleiner
Copy link
Contributor

michalkleiner commented Jul 19, 2019

Good call @jonom. I personally wish these things were thought of before introducing the React UI in the backend and/or before it was made the only option. So many issues with it to be resolved — this, any non-react-abled fields don't work 100%, state persistence issues, inline validation issues etc.

¯\_(ツ)_/¯

@jonom
Copy link
Author

jonom commented Jul 19, 2019

@michalkleiner I feel you but I think it's probably a very tough balancing act. A massive amount of work has gone in to SS4 and is still going in to it, so I think if the team had waited until everything they wanted to do was complete and super stable, we would still be waiting for SS4.0-rc1 😄 SilverStripe is going through a transition phase, like puberty. There will be some pimples and awkward moments on the way but perhaps it's a necessary step before maturity 😂

That said, I have to admit as well that I personally didn't go very deep when trying out pre-releases of SS4. If I had invested more time in trying to convert some of my existing sites to SS4 before 4.0 shipped (rather than just firing composer create-project and playing around) I might have been able to surface some of these issues earlier so they could be addressed before the APIs went stable. Maybe for SS5, upgrade tools could be prioritised and available before 5.0 is released. That might lead to deeper and broader (more developers and more websites) testing against real world SS4 websites and development patterns, and we might see more of these issues raised and addressed before going stable. I think part of the problem is that a large part of the community (myself included) waits until at least .1 is released before getting seriously engaged with new major versions, as they want to wait until major issues and bugs have been ironed out. But by then we're locked in to APIs and can't break them.

That was some pretty meta chatter, sorry! But maybe some points to consider in there.

@michalkleiner
Copy link
Contributor

@jonom yeah, I do agree with the sentiment. But to be honest, at the same time, SS went through all that child imperfections and puberty etc. with version 3. I'd hope every major version doesn't equal a newborn baby with all its issues :-D

@maxime-rainville
Copy link
Contributor

I was looking at how you would actually do this. The logic would look something like this:

  • create a lang folder with a JS file matching your target locale (e.g.: app/lang/en.js)
  • add the translation you want to override in your file like this
    ss.i18n.addDictionary('en', {
        "AssetAdmin.ADD_FOLDER_BUTTON": "Add directory",
    });
  • expose your lang folder in your composer.json file and run a composer vendor-expose
  • Tell SilverStripe to look in your lang folder for translation files when requiring translations with something like this: Requirements::add_i18n_javascript('app/lang', false);

The tricky bit I'm not 100% sure about is where to put that Requirements statement, because it needs to execute after the core SilverStripe one, otherwise our custom translation file will be loaded before the official one and our translation will be over-ridden.

@jonom
Copy link
Author

jonom commented Aug 22, 2019

@maxime-rainville I could be wrong but if you look at the example I posted, I think the problem is that individual components such as the UploadField re-load their own dictionary each time they are mounted, which would reset any fields that had been overridden using the technique you posted?

@robbieaverill
Copy link
Contributor

That shouldn't be how it works - while components will import i18n from 'i18n' and call i18n._t(), the global dictionaries are stores on window.ss.i18n in global state

@jonom
Copy link
Author

jonom commented Aug 22, 2019

That shouldn't be how it works - while components will import i18n from 'i18n' and call i18n._t(), the global dictionaries are stores on window.ss.i18n in global state

@robbieaverill Yeah experimenting again I think I was wrong about that. I knew this was in the global state but my overrides were getting overridden and I must have made an assumption about the module dictionaries getting loaded more than once, but as @maxime-rainville suggested it was probably just an execution order issue. Have updated my gross hack accordingly.

The tricky bit I'm not 100% sure about is where to put that Requirements statement, because it needs to execute after the core SilverStripe one, otherwise our custom translation file will be loaded before the official one and our translation will be over-ridden.

@maxime-rainville Maybe time for Requirements methods to have an $after option?

@maxime-rainville
Copy link
Contributor

maxime-rainville commented Aug 25, 2019

Just had a quick look and we don't provide a nice elegant way to specify the priority scripts should be loaded.

LeftAndMain does have a init hook that could be use to apply an extension to a specific controller to load additional language file. This might not work, because some of the LeftAndMain children load their JS in their own overloaded init method ... so their Requirements calls will be run after the init hook. See AssetAdmin.

LeftAndMain does provide a extra_requirements_javascript and a extra_requirements_css config option. I'm thinking we should introduce a extra_requirements_i18n_javascript option and make sure all controllers extending LeftAndMain use those config options rather than override the init method.

@jonom
Copy link
Author

jonom commented Aug 26, 2019

Using yml config for these things should automatically give us some control about execution order through the before/after property, so that sounds like a solid idea. Maybe we could go bigger though and address the limitations in the architecture.

JS Requirements execution order

I feel like the Requirements API itself could use more granular control about the order of items, especially for javascript where execution order can be critical. There are about 5 different methods on Requirements for adding javascript, and if I recall correctly the method you use influences the execution order (e.g. all custom scripts are grouped together). In my mind you should be able to specify where a script goes regardless of what 'kind' of script it is.

Could JS requirements be refactored so that any registered script has a namespaced key (could just be the file path I guess) and you can insertBefore, insertAfter, replace, remove, block etc.? And perhaps we could re-use the before/after logic from the yaml config so that include/execution order of scripts isn't determined until it's rendered in to the response, allowing us to specify that script X should come after script Y even if script Y hasn't been registered yet?

One API for translations

I really think it would be better if all translations were provided by yaml files so devs don't have to learn and use two systems. There are tools like js-yaml that can parse yaml to js if that needs to be part of a build or test process for individual modules. But I think the overall dev/build process should take care of compiling a single js SS dictionary file for each language from yaml files, which would take in to account any user overrides.

@maxime-rainville
Copy link
Contributor

I like the "One API for translations" idea, but that would probably require a breaking change, so it would have to go in the next major release.

Adding extra parameters and/or methods to control the order things get inserted could probably be done in a minor, but that probably require a wider conversation.

Would you like to draft RFCs for those ideas.

In the short term, I think we can make it easier for people to override our translations by being a bit more aware of how we include JS files in our LeftAndMain subclasses.

@robbieaverill
Copy link
Contributor

When considering the LeftAndMain ideas thrown around here, keep in mind that JavaScript translations (the same as YAML) can be used on the frontend as well as in the CMS

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants