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

OBPIH-5161+5246 Make user transition in/out of translation mode smoother #3738

Merged
merged 12 commits into from
Jan 5, 2023

Conversation

kchelstowski
Copy link
Collaborator

  • getting rid of useDebugLocale logic, which Justin mentioned in the ticket
  • not allowing user to set her/his default locale to translation mode locale
  • the localization mode is activated automatically when changing the language for Acoli
  • the localization mode is deactivated when changing language to any other than Acoli when being in localization mode
  • when disabling localization mode by button, we are brought back to the locale, that we had set before enabling localization mode

Bonus:

  • changing the webpack config, so all babel's and eslint's stuff works also fine for .js files, not only .jsx - example: optional chaining or nullish coalescing operator, which were added by me not a long time ago, but were not working for .js files

key={language.code}
href="/openboxes/user/enableLocalizationMode"
>
<Translate id={`react.default.${language.name.toLowerCase()}.label`} /> {''}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is {''} necessary?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Probably not, but since it has not been code that I've written, I did not catch such details

Comment on lines +109 to +111
const mapDispatchToProps = {
changeLocale: changeCurrentLocale,
setLanguage: setActiveLanguage,
Copy link
Collaborator

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 using useDispatch() instead of mapDispatchToProps? I found an interesting discussion on stackoverflow about this two approach Should I use useselector/useDispatch instead of mapStateToProps and i am curious what do you think

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I don't see any pros/cons of using one versus another. I think it's rather what you prefer and what is more useful in each scenario (for example you can't use mapDispatchToProps inside a custom hook, so you are obligated to use useDispatch().
What I'm sure about is that we shouldn't mix both if possible and probably in "regular" components stick with connect approach as we've done for whole project, as it might be easier to read and debug, because we are used to this convention.
@drodzewicz any additional thoughts?

@jmiranda
Copy link
Member

I need a bit more time to review this one.

@kchelstowski
Copy link
Collaborator Author

Marking it as a draft for a while, as I will probably want to bring back useDebugLocale for some cases, because creating vars with locale == translationModeLocale for many GSP pages does not seem the best idea for me.

@kchelstowski
Copy link
Collaborator Author

@jmiranda it's now ready for review. I've brought back useDebugLocale in the latest commit for some use cases, let me know if it's better to keep it like I did, or remove it completely and inject logic locale == translationModeLocale for each GSP page where we used to use if (session.useDebugLocale).
I personally believe that the latest version should be more readable and less complexed, but I leave it for you to decide :)

@@ -88,6 +88,10 @@ class ApiController {
def localizationMode
def locale = localizationService.getCurrentLocale()
Object[] emptyArgs = [] as Object []
def translationModeLocale = grailsApplication.config.openboxes.locale.translationModeLocale
// If current locale is equal to translation mode locale, we are in localization mode
session.useDebugLocale = locale == new Locale(translationModeLocale)
Copy link
Member

Choose a reason for hiding this comment

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

Instead of sprinkling this logic everywhere, we should probably have it done in a filter. InitializationFilters seems to be the best candidate but I'm open to suggestions.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

sounds good to me, I haven't thought about such a solution

@@ -57,12 +57,12 @@ class ApiController {

@CacheFlush(["megamenuCache"])
def chooseLocale = {
Locale locale = localizationService.getLocale(params.id)
if (!locale) {
Locale newLocale = localizationService.getLocale(params.id)
Copy link
Member

Choose a reason for hiding this comment

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

This seems unnecessary.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

what do you mean as unnecessary here? I'm a bit confused, because I only changed the naming from locale to newLocale and I'm not sure if either changing of name seems unnecessary or this part: localizationService.getLocale(params.id)

Copy link
Member

Choose a reason for hiding this comment

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

I meant changing the variable name isn't necessary and I want to avoid unnecessary changes as much as possible since it complicates the rebase process for OBGM.

@@ -197,7 +201,8 @@ class ApiController {
currencyCode : currencyCode,
localizedHelpScoutKey: localizedHelpScoutKey,
isHelpScoutEnabled : isHelpScoutEnabled,
localizationModeEnabled : localizationModeEnabled
localizationModeEnabled : localizationModeEnabled,
translationModeLocale : translationModeLocale
Copy link
Member

Choose a reason for hiding this comment

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

This is my fault since I kept referring to this as "translation mode", but let's either use the existing name "localization mode" or convert everything to "translation mode".

// If we are in localization mode and want to change the language through the footer on React side
// we want to make sure we set the language to the clicked one, not for the previous locale
// we want to set the prev locale only if we disable localization mode through "Disable localization mode" button
Locale localeToSet = params?.localeToSet ? new Locale(params.localeToSet) : session?.prevLocale
Copy link
Member

Choose a reason for hiding this comment

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

Let's just call this request parameter and variable "locale" (not localeToSet) since that's what it's been called. I'm worried that adding a new request parameter will cause confusion.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

For now it is only used for React part, as I didn't have an idea how to determine whether I disable localization mode through button "Disable localization mode" or I just change the language.
When disabling the localization mode through button "Disable localization mode" we want to go back to previous chosen locale.
When changing the language while being in localization mode, we want to disable localization mode, but we don't want to go back to previous chosen locale, but to change it for the language we've just clicked, so that's why I had to figure out some way to determine this.
For GSP part it's unnecessary, because we always change the language through updateUserAuthLocale.
For React part I just add something like this when changing the language being in localization mode:

 href={`/openboxes/user/disableLocalizationMode?localeToSet=${language.code}`}

I know it may cause confusion, but I couldn't figure out anything better and that's why I make the checking if this param is provided and that's why I added a comment above this.
If you have any idea how I could deal with this case, I'd love to hear from you

}

def enableLocalizationMode = {
// We want to store the previous locale, so we can go back to it when disabling localization mode through button
session.prevLocale = localizationService.getCurrentLocale()
Copy link
Member

Choose a reason for hiding this comment

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

Sorry to be nit-picky but let's avoid using abbreviations. session.previousLocale is more readable.

Copy link
Member

Choose a reason for hiding this comment

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

Also is there any difference between getting the current locale versus using session.locale?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah, let me explain - when you log in and do not neither change the language nor enable localization mode before, session.locale is still null - your "initial" locale is taken from session.user.locale. We set the session.locale when we either change the language or like you see line below, we enable the localization mode.
This is why I modified getCurrentLocale() method, because at the beginning, we do not have session.locale

// if no locale specified, do nothing
if (!params.locale) {
redirect(controller: "dashboard", action: "index")
}
Copy link
Member

Choose a reason for hiding this comment

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

I know this isn't code you wrote, but we should probably remove the redirects since that's handled at the end. And instead of handling the

    if (params.locale) { 
        // convert the passed locale parameter to an actual locale
        Locale locale = new Locale(params.locale)
    
        if (locale) { 
            // Change the locale
            session.locale = locale
        
            // remove all redirects since they aren't going to do anything 
        
            // Check if chosen locale is equal to translation mode locale, if so, enable localization mode
            session.useDebugLocale = localizationService.isLocalizationModeEnabled() 
        }
    }

    log.info "Redirecting to " + params?.targetUri
    if (params?.targetUri) {
        redirect(uri: params.targetUri)
        return
    }
    // redirect to the dashboard
    redirect(controller: "dashboard", action: "index")

LocalizationService

isLocalizationModeEnabled() { 
    def localizationModeLocale = new Locale(grailsApplication.config.openboxes.locale.translationModeLocale)
    return currentLocale == localizationModeLocale

@@ -28,7 +28,7 @@ class MessageTagLib {
boolean databaseStoreEnabled = grailsApplication.config.openboxes.locale.custom.enabled
if (!databaseStoreEnabled) {
Locale defaultLocale = new Locale(grailsApplication.config.openboxes.locale.defaultLocale)
attrs.locale = attrs.locale ?: session?.user?.locale ?: session.locale ?: defaultLocale
attrs.locale = attrs.locale ?: session?.locale ?: session?.user?.locale ?: defaultLocale
Copy link
Member

Choose a reason for hiding this comment

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

👍

// First search for session locale, if none set, then search for user's default locale, otherwise get the default locale
def session = RequestContextHolder.currentRequestAttributes().getSession()
if (session?.locale) {
return session.locale
Copy link
Member

Choose a reason for hiding this comment

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

👍

@jmiranda
Copy link
Member

jmiranda commented Jan 4, 2023

chooseLocale should implement the same rules. but @kchelstowski says he's implemented the rules properly in the initialization filter. there's a weird case where the state is not set until we get to the controller and therefore if there's logic that depends on that state in the filters, then we'll have one request where the logic will be wrong. I'll pull this down and check it.

@kchelstowski
Copy link
Collaborator Author

kchelstowski commented Jan 5, 2023

@jmiranda I played with it again and I reminded myself why I couldn't just stick with the chooseLocale for every case: changing language, disabling/enabling localization mode for the React side. The problem is that api requests don't refresh the page in this case - it just dispatches an action in "a React way" and the problem is that we inject the crowdin script in the main.gsp file - if we are in localization mode, it injects the script, if we are not, it doesn't and for api requests it doesn't trigger the main.gsp to "reload" - it only happens if you refresh the page (which happens when you click a link that redirects us to the enable/disable localization mode function).

 <g:if test="${session.useDebugLocale}">
        <script type="text/javascript">
            var _jipt = [];
            _jipt.push(['project', 'openboxes']);
        </script>
        <script type="text/javascript" src="//cdn.crowdin.com/jipt/jipt.js"></script>
    </g:if>

So I believe that if we'd like to stick with the chooseLocale for React for each of those cases, we should find a way to inject the crowdin script inside React, because now it is handled by GSP.
Saying that, I think for now it's ready to be merged unless you have some more questions

@jmiranda jmiranda merged commit 6bfc10d into develop Jan 5, 2023
@jmiranda jmiranda deleted the OBPIH-5161 branch January 5, 2023 16:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants