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

extending Polymer.AppLocalizeBehavior #97

Open
1 of 8 tasks
sangron opened this issue May 24, 2017 · 5 comments
Open
1 of 8 tasks

extending Polymer.AppLocalizeBehavior #97

sangron opened this issue May 24, 2017 · 5 comments

Comments

@sangron
Copy link

sangron commented May 24, 2017

Description

Creating a mixin/behavior to be extended by multiple elements, the this.loadResources('/src/locales.json') is not setting the resources property, so this.localize('res.key') returns undefined

Expected outcome

resources being set with the object in the json file

Actual outcome

resources stays undefined

Steps to reproduce

formats-behavior.html

<link rel="import" href="../../bower_components/app-localize-behavior/app-localize-behavior.html">

<script>
FormatMixin = function(superClass) {

  return class extends Polymer.mixinBehaviors([Polymer.AppLocalizeBehavior], superClass) {

    constructor() {
      super();
      this.loadResources(this.resolveUrl('/src/locales.json'));
    }

    static get properties() {
      return {
        language: {
          value: 'es'
        },
        formats: {
          type: Object,
          value: function() {
            return {
              number: { MXN: { style: 'currency', currency: 'MXN' } }
            };
          }
        }
      }
    }

    getLabel(a) {
      var args = Array.prototype.slice.call(arguments);
      var prop = args.join('');
      var label = prop;
      if(this.localize)
        label = this.localize(prop);

      return label;
    }

  }
}
</script>

then in file legal-docs.html

<link rel="import" href="mixins/formats-behavior.html">
<dom-module id="legal-docs">
  <template>
    <div>[[getLabel('app.title')]]</div>
  </template>
  <script>
    class LegalDocs extends FormatMixin(Polymer.Element) {

      static get is() { return 'legal-docs'; }

    }

    window.customElements.define(LegalDocs.is, LegalDocs);
  </script>
</dom-module>

and locales.json

{
  "es" : {
    "app.title" : "Doctos. Legales"
  }
}

I have debugged it and the constructor is being called, the json file is being loaded, confirming in the network tab in the devtools. But the resource property is not set.
I have another app with this same pattern without problems, but it is using an earlier version of Polymer 2, so I am just guessing that something broke in recent changes.

Browsers Affected

  • Chrome
  • Firefox
  • Safari 9
  • Safari 8
  • Safari 7
  • Edge
  • IE 11
  • IE 10
@notwaldorf
Copy link
Contributor

notwaldorf commented Jun 12, 2017

Have you tried loading the resources later, in ready or connectedCallback? constructor is way before Polymer stamped the properties, so it makes sense why resources is not populated

@vtellier
Copy link

vtellier commented Jun 14, 2017

Edit 27.06.2017: The issue happen when you don't lazy load your pages.

I have the same problem than @sangron: undefined resources attribute.

I implemented his code (I changed only names) to see what do we get when loading in constructor, ready in connectedCallback.

Listening to app-localize-resources-loaded

I used the event app-localize-resources-loaded to know when the resources are really loaded. I added few traces in the console to have an idea of the actual sequence:

FormatMixin:

    connectedCallback() {
      super.connectedCallback();
      
      console.log("Add a listener to app-localize-resources-loaded");
      this.addEventListener('app-localize-resources-loaded', function(e){
        console.log('app-localize-resources-loaded', this.resources);
      }.bind(this));
      
      console.log('Calling this.loadResources()...');
      this.loadResources(this.resolveUrl('/static/locales.json'));
    }

    static get properties() {
      // ...
    }

    getLabel(a) {
      
      console.log('getLabel() is called... this.resources is', this.resources);
      var args = Array.prototype.slice.call(arguments);
      var prop = args.join('');
      var label = prop;
      if(this.localize)
        label = this.localize(prop);

      return label;
    }

In the constructor

mi-localization.html:12 Add a listener to app-localize-resources-loaded
mi-localization.html:17 Calling this.loadResources()...
mi-localization.html:39 getLabel() is called... this.resources is undefined
mi-localization.html:14 app-localize-resources-loaded Object {es: Object}

I added a <pre>{{resources}}</pre> so I can see if the binding works even though the call is performed in the constructor. I see the [Object object] rendered as expected because the result of the iron-ajax takes a bit of time and everything is ready-to-go when the response arrives.
So in my opinion the properties will be stamped in most of cases.

In ready

getLabel() is called... this.resources is undefined
mi-localization.html:12 Add a listener to app-localize-resources-loaded
mi-localization.html:17 Calling this.loadResources()...
mi-localization.html:14 app-localize-resources-loaded Object {es: Object}

The compound binding call the localize method even before we do request for the resources.
The problem is then "how to delay the compound binding so we can trigger it when the resources arrived?"

In connectedCallback

getLabel() is called... this.resources is undefined
mi-localization.html:12 Add a listener to app-localize-resources-loaded
mi-localization.html:17 Calling this.loadResources()...
mi-localization.html:14 app-localize-resources-loaded Object {es: Object}

The same...

Effective (but boring) workaround

To get it to work for sure, I use an intermediary private property in the compound binding instead of the synchronous call to the localize method.
I feed this private property in the listener of app-localize-resources-loaded.

class OrdhusetApp extends LocalizationMixin(Polymer.Element) {
      static get is() { return 'ordhuset-app'; }
      static get properties() {
        return {
          __translation: {
            type: String,
            value: 'no translation :('
          }
        };
      }
      
    ready() {
        this.addEventListener('app-localize-resources-loaded', function(e){
          this.__translation = this.getLabel('app.title');
        }.bind(this));
        
        // Call the super.ready method after having set the listener
        // so i'm sure it is here before the resources arrive.
        super.ready();
      }
    }

And the compound binding:

<div>Test translation: [[__translation]]</div>

Result: image

It works... But it's not really convenient!

vtellier pushed a commit to vtellier/ordhuset that referenced this issue Jun 14, 2017
@BennerGe
Copy link

BennerGe commented Nov 20, 2017

@notwaldorf What is the status of this issue? Still not working here.

@rikbrowning
Copy link

The issue is trying to load the resources globally in the app using load resources. Child components load and are created before the network request is returning. When the network request does finally return the _computedLocalize function never gets triggered in the child component as the resources have been updated outside of the child component so it is not aware of the changes. One potential solution could be that any time _computeLocalize gets called and there is an existing ajax request to listen for the response of it and update then. That way all localizations should be triggered when the one ajax response occurs.

@dsyrstad
Copy link

dsyrstad commented May 7, 2018

I find myself having to do this in all localized elements:

<dom-module id="my-element">
    <template>
       <style>...</style>
        <!-- Don't render until I18N resources are ready. -->
        <dom-if if="{{resources}}">
            <template>
                 ... all of my element's DOM...
            </template>
        </dom-if> <!-- resources -->
    </template>
   <script>
        class MyElement extends Polymer.mixinBehaviors([Polymer.AppLocalizeBehavior], ReduxActionsMixin(Polymer.Element)) {
    ...
   </script>
</dom-module>

This waits to construct any of the element's DOM until {{resources}} is non-empty/truthy. If I don't do this, I get localize() called before the resources are ready.

I'm using PolymerElements/app-localize-behavior 2.0.1.

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

6 participants