Closed t2ym closed 5 years ago
lit-html
lit-html
import { html, i18n, bind } from 'i18n-element/i18n.js';
class MyI18nElement extends i18n(HTMLElement) {
... // a few boilerplate mandatory methods are omitted here
render() {
return html`${bind(this, 'my-i18n-element')}
<span>localizable message with ${this.property}</span>`;
}
...
}
class ExtendedElement extends MyI18nElement {
render() {
return html`${bind(this, 'extended-element')}
<div>extended message with ${this.property}</div>
${super.render()}`;
}
}
class CompositeElement extends i18n(HTMLElement) {
render() {
return html`${bind(this /* bound to 'composite-element' */)}
<div>composite element with ${getMessage()}</div>
<extended-element></extended-element>`;
}
}
const binding = bind('get-message', import.meta); // bound to a pseudo-element name
const getMessage = () => html`${'get-message', binding}<span>get message</span>`;
locales/{element-name}.{locale}.json
npm install i18n-element@3.0.0-rc.1
or npm install i18n-element
lit-html@^1.0.0
import { html, i18n, bind } from 'i18n-element/i18n.js'
/demo/preprocess/
demo/clock/
polymer serve --npm --module-resolution=node
polymer build
build/{esm-unbundled|esm-bundled|es6-bundled|es5-bundled}
npm run demo
npm run demo -- locales --targets="de es fr ja zh-Hans"
npm run demo
must be run again after this command)package-lock.json
issue
npm
may unexpectedly OVERRIDE CUSTOM PACKAGES FROM GitHub with official ones in npm install
on certain conditions. Custom packages in package-lock.json
must be checked after each npm install
operation.package-lock.json
is corrupted, npm install --save-dev github:t2ym/espree#es2018 github:t2ym/escodegen#es2018
should fix the corruption.package-lock.json
is not corrupted, npm ci
is strongly recommended for dependency installationLitElement
as base classesi18n-behavior/i18n-behavior.js
3.0.0-rc.2
BehaviorsStore._I18nBehavior
LitElement
but compatible with LitElement
Binding
via bind()
pseudo-directive
this
: ${bind(this)}
CamelCasing
to generate the corresponding custom element name like camel-casing
${bind('unique-pseudo-element-name', import.meta)}
binding
, the pseudo-directive has to be a sequence expression starting with a literal string for the name so that static preprocessor can easily detect the bound name for the template
${'unique-pseudo-element-name',binding}
this
with a unique name: ${bind(this, 'this-element-name')}
render()
can be used by the extended class as ${super.render()}
'lang-updated'
event can be captured via bind().element.addEventListener('lang-updated',...)
cd demo; ../node_modules/.bin/gulp
@license
comments to preprocessed JavaScriptsnpm run demo -- locales --targets="de es fr ja zh-Hans"
# populate empty locales/*.{de|es|fr|ja|zh-Hans}.json
# existing JSON files are not touched
npm run demo -- locales --targets="de es fr ja zh-Hans"
# merge en resources into localized ones
npm run demo
# translate demo/xliff/bundle.{de|es|fr|ja|zh-Hans}.xlf
# merge XLIFF translations into localized JSON files
npm run demo
html
process for a non-preprocessed raw template literal is as fast as its corresponding preprocessed one after the first call of html
.strings.shift(); parts.shift();
preprocessedStrings
and preprocessedParts
are cachedUncamelCase()
boundElement.lang
updatestemplateDefaultLang
binding.element.templateDefaultLang
, defaulting to <html lang>
, then en
fire()
browser | raw | preprocessed | bundled | coverage |
---|---|---|---|---|
Chrome 72 | ✔︎ | ✔︎ | ✔︎ | ✔︎ |
Firefox 65 | ✔︎ | ✔︎ | ✔︎ | ✔︎ |
Safari 12 | ✔︎ | ✔︎ | ✔︎ | ✔︎ |
Safari 9 | ✔︎ | ✔︎ | ✔︎ | ✔︎ |
Edge 17 | ✔︎ | ✔︎ | ✔︎ | ✔︎ |
IE 11 | ✔︎ | ✔︎ | ✔︎ | ✔︎ |
lit-html
syntax handling
@event=${function handler(event) {...}}
.property=${property_value}
attribute=${attribute_value}
?boolean-attribute=${true_or_false}
attr$="${attr}"
-> attr="${attr}"
<svg>...</svg>
with ${svg`...`}polyserve --npm --module-resolution=node
const binding = bind('get-message', import.meta);
html([
'<!-- localizable -->',
'<div>',
'</div><div>',
'</div>'
], ...bind(('get-message', binding), (_bind, text, model, effectiveLang) => [
_bind,
text['div'],
getMutatingMessage()
], {
'meta': {},
'model': {},
'div': 'message'
}));
html([
'<!-- localizable -->',
'\n <style>\n :host {\n display: block;\n width: 100%;\n }\n world-clock {\n display: flow;\n max-width: 300px;\n }\n </style>\n <div>',
'</div>\n ',
'\n '
], ...bind(this, (_bind, text, model, effectiveLang) => [
_bind,
text['div_1'],
repeat(this.timezones, item => item, (item, index) => html`<world-clock .timezone=${ item }></world-clock>`)
], {
'meta': {},
'model': {},
'div_1': 'World Clocks'
}));
html([
'<!-- localizable -->',
'\n <style>\n :host {\n display: block;\n width: 100%;\n max-width: 350px;\n padding: 2px;\n }\n </style>\n <div><i18n-format lang="',
'"><span>',
'</span><span slot="1">',
'</span><button @click="',
'" slot="2">',
'</button><button @click="',
'" slot="3">',
'</button></i18n-format></div>\n ',
'\n '
], ...bind(this, 'world-clock', (_bind, text, model, effectiveLang) => [
_bind,
effectiveLang,
text['div_1']['0'],
(this.timezone < 0 ? '' : '+') + this.timezone / 60,
() => this.timezone -= 60,
text['div_1']['2'],
() => this.timezone += 60,
text['div_1']['3'],
super.render()
], {
'meta': {},
'model': {},
'div_1': [
' Timezone: GMT{1}\n {2}\n {3} ',
'{{parts.0}}',
'-1h',
'+1h'
]
}));
html([
'<!-- localizable -->',
'\n <style>\n :host {\n display: block;\n }\n .square {\n position: relative;\n width: 100%;\n height: 0;\n padding-bottom: 100%;\n }\n \n svg {\n position: absolute;\n width: 100%;\n height: 100%;\n }\n \n .clock-face {\n stroke: #333;\n fill: white;\n }\n \n .minor {\n stroke: #999;\n stroke-width: 0.5;\n }\n \n .major {\n stroke: #333;\n stroke-width: 1;\n }\n \n .hour {\n stroke: #333;\n }\n \n .minute {\n stroke: #666;\n }\n \n .second, .second-counterweight {\n stroke: rgb(180,0,0);\n }\n \n .second-counterweight {\n stroke-width: 3;\n }\n </style>\n <div id="target" @click="',
'" .property="',
'" attr="',
'" ?enabled-boolean-attr="',
'" ?disabled-boolean-attr="',
'" i18n-target-attr="',
'"><i18n-format lang="',
'"><span>',
'</span><span slot="1">',
'</span><span slot="2">',
'</span></i18n-format></div>\n <div>',
'</div>\n <div class="square"> <!-- so the SVG keeps its aspect ratio -->\n \n <svg viewBox="0 0 100 100">\n \n <!-- first create a group and move it to 50,50 so\n all co-ords are relative to the center -->\n <g transform="translate(50,50)">\n <circle class="clock-face" r="48"></circle>\n <g>',
'</g><!-- g tag to avoid i18n-format conversion -->\n <g>',
'</g><!-- g tag to avoid i18n-format conversion -->\n\n <!-- hour hand -->\n <line class="hour" y1="2" y2="-20" transform="rotate(',
')"></line>\n \n <!-- minute hand -->\n <line class="minute" y1="4" y2="-30" transform="rotate(',
')"></line>\n \n <!-- second hand -->\n <g transform="rotate(',
')">\n <line class="second" y1="10" y2="-38"></line>\n <line class="second-counterweight" y1="10" y2="2"></line>\n </g>\n </g>\n </svg>\n </div>\n '
], ...bind(this, 'lit-clock', (_bind, text, model, effectiveLang) => [
_bind,
event => {
let div = event.composedPath().filter(el => el.id === 'target')[0];
alert('div.outerHTML = ' + div.outerHTML + ' div.property = ' + div.property + ' div.getAttribute("attr") = ' + div.getAttribute('attr') + ' div.getAttribute("i18n-target-attr") = ' + div.getAttribute('i18n-target-attr'));
},
'property value',
'attr value',
true,
false,
model['target']['i18n-target-attr'],
effectiveLang,
text['target']['0'],
this.date.getHours(),
this.date.getMinutes(),
getMessage(),
minuteTicks,
hourTicks,
30 * this.date.getHours() + this.date.getMinutes() / 2,
6 * this.date.getMinutes() + this.date.getSeconds() / 10,
6 * this.date.getSeconds()
], {
'meta': {},
'model': { 'target': { 'i18n-target-attr': 'I18N target attribute value' } },
'target': [
'Time: {1}:{2}',
'{{parts.5}}',
'{{parts.6}}'
]
}));
polyfill.js
moduleattributeChangedCallback
calls
attributedChangedCallback
via setAttribute()
for polyfilled custom elements v1attributedChangedCallback
via setAttributeNS()
for polyfilled custom elements v1templateDefaultLang
is ineffective_I18nBehavior.templateDefaultLang
is unexpectedly set
this
object for _constructDefaultBundle()
callsfire()
is not fully compatible with that in Polymer library.
fire()
of Polymer library preciselypackage-lock.json
can be easily corrupted by some npm install
commands
github:t2ym/espree#es2018
and github:t2ym/escodegen#es2018
are NOT installed although they are clearly specified in package.json
and corrupted package-lock.json
is generated with other official versions of espree
and escodegen
node_modules/
and package-lock.json
npm ci
with the fixed package-lock.json
npm install
in certain conditions. Custom packages in package-lock.json
must be checked after each npm install
operation.<i18n-format>
{{serialize(text.id.0)}}
is converted to ${JSON.stringify(text['id']['0'])}
i18n-behavior@3.0.0-pre.18
or later is requiredi18n-format@3.0.0-pre.12
or later is requiredi18n-number@3.0.0-pre.9
or later is requireddemo/clock/clock.js
this.timezones
are not propagated to UI for now.
<i18n-format>
<json-data>{
"0": "No timezones",
"1": "Only 1 timezone for {2} is shown.",
"one": "{1} timezone other than {2} is shown.",
"other": "{1} timezones other than {2} are shown."
}</json-data>
<i18n-number offset="1">${this.timezones.length}</i18n-number>
<span>${'GMT' + (this.timezones[0] < 0 ? '' : '+') + (this.timezones[0] / 60)}</span>
</i18n-format>
<div i18n-target-attr="attribute with ${param1} and ${param2}">
<div i18n-target-attr$="{{i18nFormat(model.div_1.i18n-target.attr.0,parts.1,parts.2)}}">
<div i18n-target-attr="${_bind.element.i18nFormat(model['div_1']['i18n-target-attr']['0'],param1,param2)}">
lang
attribute/property becomes empty (""
) when fallback language is ""
lang
and effectiveLang
value on lang-updated
events<!-- localizable -->${bind(this)}
really required for preprocessed templates? : Yes
lit-html
as they are like this?
element._processTasks()
has to be called on rendering
const binding = bind('get-message', import.meta);
html([
'<div>',
'</div><div>',
'</div>'
], ...bind(('get-message', binding), (text, model, effectiveLang) => [
text['div'],
getMutatingMessage()
], {
'meta': {},
'model': {},
'div': 'message'
}));
npm install --save-dev github:t2ym/espree#es2018
npm install --save-dev github:t2ym/escodegen#es2018
effectiveLang
is not set
efffectiveLang
on lang-updated
eventsi18n-preference
is not attached properly on load event
i18n-behavior@3.0.0-pre.14
i18n-format
is unexpectedly applied within inline SVG
i18n-format
conversion must be skipped within inline SVG elements<g>
tag<svg>...</svg>
tag with ${svg...
}attributeChangedCallback()
on Safari 9_processTasks()
for multiple instances as well as the first one<world-clock>
does not observe html.lang
at the first load. If html.lang
is manually updated, it mirrors the lang
value.
this.lang
is en
and previous value is empty, it follows the html.lang
valuesuper.attributeChangedCallback
is checked before typeof super.attributeChangedCallback === 'function'
is checked
typeof super.attributeChangedCallback === 'function'
even when super.attributeChangedCallback === undefined
, which is controversial but actual with Edge 18<simple-attribute-element>
's shadowRoot
is accessedgulp patch-browser-capabilities
before testingtransform
attributes in SVG are unexpectedly set as none
instead of variables
transform=
for x-transform-x=
to avoid the issueattributeChangedCallback
is not called
attributeChangedCallback
with MutationObserver
[]
Set
in IE 11 does not support new Set(['set', 'of', 'initial', 'values'])
Set
in IE 11 does not support iteratorsnew Set()
and [].concat(super.observedAttributes || [])
and set.add()
i18n.js
clock.js
test/{element classes}.js
Set
class constructor
, iterator
, keys
, values
Array.from()
, which is required for Set
object iteration in [...obj]
Map
class in sync with Set
classconstructor
, iterator
, keys
, values
, entries
MutationObserver.observe
's attributeFilter: []
enables all attributes instead of no attributes
observedAttributes
properlynew.target
this.constructor
is acceptable[].includes(s)
[].indexOf(s) >= 0
@click .prop ?boolean
are invalid attribute names@webcomponents/webcomponentsjs/webcomponents-{loader|bundle}.js
does not polyfill these missing properties in IE 11
DocumentFragment.prorotype.children
SVGElement.prototype.children
@webcomponents/webcomponentsjs/webcomponents-{loader|bundle}.js
does not polyfill attributeChangedCallback()
properly in IE 11
attributeChangedCallback()
is not called on this.lang mutation in IE 11
[3.0] Support lit-html and LitElement
Tasks
lit-html
- 3.0.0-rc.1 see belowNotes