Friday, December 27, 2013

Can't link import in Polymer.dart


I have what I think is a fairly nice localization implementation for Polymer. Only it doesn't work in Dart. At least I don't think that it does.

A localized Polymer in my scheme looks something like:
    <link rel="import" href="packages/i18n_example/hello-you.html">
    <link rel="import" href="packages/i18n_example/hello-you-en.json">
    <link rel="import" href="packages/i18n_example/hello-you-fr.json">
    <link rel="import" href="packages/i18n_example/hello-you-es.json">
How a localized Polymer is coded by a web developer is almost inconsequential to me at this point. The JSON files can be supplied by the developer as above or can be bundled in the Polymer's <template>. What interests me more right now is getting the non-Polmer <link> imports working under Dart. And I think that means getting to know Dart Pub transformers better.

Transformers are used to build Dart applications for deployment. They also run locally with the pub serve local development web server. This local feature is nice in that it runs an application just like it would for deployment, but allows for rapid code updates. Unfortunately, I think the Polymer transformer is stripping out my <link> imports. To verify, I start by commenting out the transformer from my pubspec.yaml:
name: i18n_example
dependencies:
  polymer: any
dev_dependencies:
  unittest: any
# transformers:
# - polymer:
#     entry_points: web/index.html
And, yup, that fixes my problem. Instead of the large gap between the components and the transformed Polymer application that I had been seeing:
    <!-- Load component(s) -->
    
    
    
    
    <!-- Load Polymer -->
    <script type="application/dart" src="index.html_bootstrap.dart"></script>
I now see my Polymer along with the JSON configuration files that I wanted to see:
    <!-- Load component(s) -->
    <link rel="import" href="packages/i18n_example/hello-you.html">
    <link rel="import" href="packages/i18n_example/hello-you-en.json">
    <link rel="import" href="packages/i18n_example/hello-you-fr.json">
    <link rel="import" href="packages/i18n_example/hello-you-es.json">
    <!-- Load Polymer -->
    <script type="application/dart">
      export 'package:polymer/init.dart';
    </script>
Now that I have access to my JSON localizations, I re-implement last night's JavaScript load-from-import in Dart:
@CustomTag('hello-you')
class HelloYou extends PolymerElement {
  // ...
  @published String locale = 'en';
  Map locales = {};

  HelloYou.created(): super.created();

  ready() {
    super.ready();

    var re = new RegExp(r'hello-you-(\w+).json');
    var list = ownerDocument.
      querySelectorAll('link[rel=import]').
      where((el)=> el.href.contains(re));

    list.forEach((link) {
      var json = link.import.documentElement.text;
      var results = re.firstMatch(link.href);
      locales[results[1]] = JSON.decode(json);
    });

    locales[locale].forEach((k, v)=> this[k] = v);
  }
  // ...
}
See last night's post for the gruesome details. The brief summary is: get a list of JSON <link> elements, parse their contents as JSON, populate the corresponding locale, then set various values based on the currently selected locale. To make the the this[k] assignment work in Dart, I need to define an operator method, but that is pretty simple. That aside, the implementation is nearly identical to last night's JavaScript implementation. And it works the same. Well, almost:



Interestingly, the utf-8 encoded characters are being interpreted as some kind of single-byte character set (probably iso-8859-1). Bleh. Well, that's a bit odd, but I have no idea what the proper way to access <link> imported JSON is. My link.import.documentElement.text may be a bit off the beaten path. But that does not mean that there is no way to get it back onto the utf-8 path:
    // ...
    list.forEach((link) {
      var results = re.firstMatch(link.href);
      var json = UTF8.decode(link.import.documentElement.text.codeUnits);
      locales[results[1]] = JSON.decode(json);
    });
    // ...
With that, I am in business:



Not surprisingly, if I reinstate the Polymer transformer, this no longer works. With the JSON <link> imports stripped from the document, my code no longer works. Bother. It seems I need to dig into the Polymer transformer to figure this out. That seems a topic for another day, so instead, I try an alternate approach to <link> importing.

Instead of adding the <link> tags to the document, I add them to the Polymer's template:
<polymer-element name="hello-you">
  <template>
    <div><!-- ... --></div>
    <link rel="import" href="hello-you-en.json">
    <link rel="import" href="hello-you-fr.json">
    <link rel="import" href="hello-you-es.json">
  </template>
  <script type="application/dart" src="hello_you.dart"></script>
</polymer-element>
Unfortunately, that does not work either. It seems polyfilling <link> imports is limited to the document itself because the import property of these links is always null.

Ah, well. I suppose I can convert these to <polymer-ajax> tags or something along those lines. Tomorrow.

Day #978

2 comments:

  1. Great. I made a post about this problem here:
    https://code.google.com/p/dart/issues/detail?id=15796

    ReplyDelete
  2. Internationalization in Dart it's a mess, and it's a big stumbling-block.

    We are at the verge of 2014. We need a modern internationalization system integrated right into Dart.

    Those are the features we need:

    - TRANSPARENCY : Once you write the code for a web page in a certain language, you must be able to localize it without going back and change anything of the original dart or html code. Switching between languages should be independent from the structure or the code of the page. For example a single call to a method at page load should localize automatically all strings and numerical values behind the scenes.

    - INTEGRATED : Localization data should be integrated into the components (both for native or Polymer elements). Localization data should be portable like the elements and recognized at the language level. Missing or partial localizations should be marked as error. All international standards of metric units should be included and automatically parsed from strings, dates or numbers, with explicit conversion methods when the identification is not possible.

    - AUTOMATED : The right localization, if present, should be automatically activated according to the browser language or location. The existence of localization data should be enough to make the localization happen. An explicitly statement must be provided to disable this feature. In this way just copying a new localization file for a specific language in the same folder of the web app sources should activate the localization, without changing any other file.

    ReplyDelete