Skip to content
Snippets Groups Projects
Verified Commit c40b7760 authored by Volker Schukai's avatar Volker Schukai :alien:
Browse files

chore: move tutorials to monsterjs-org

parent 8c04cdd2
No related branches found
No related tags found
No related merge requests found
Showing
with 1 addition and 1022 deletions
You'll need basic HTML, CSS, and JavaScript skills for Monster. If you're just
starting out with frontend development, you should have a good beginner's
guide open alongside this one.
You can also post your questions on [Stack Overflow](https://stackoverflow.com/questions/ask?tags=javascript,monster).
To get started with Monster, all you need is a text editor and a browser.
The best way is to copy the following example into a file and save it as
index.html.
```
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>awaken the monster in you!</title>
</head>
<body>
<div>your version is
<spay id="version"></spay>
</div>
<script type="module">
import {Version} from 'https://cdn.skypack.dev/@schukai/monster@latest/source/types/version.js';
document.getElementById('version').innerText = new Version('1.0.0').toString();
</script>
</body>
</html>
```
Now open this file with your browser.
What did you do? you used the class 'Version' of monster and created a Version object. They then output this in a span.
**Voila!**
As seen above in the version example, each monster class or function can be used independently.
Alternatively, Monster can be used as a collection of many useful classes and functions via
the `Monster` namespace as a single javascript file.
```
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>awaken the monster in you!</title>
<script src="https://cdn.skypack.dev/@schukai/monster@latest/source/monster.js"></script>
</head>
<body>
<div>your version is
<spay id="version"></spay>
</div>
<script>
document.getElementById('version').innerText = new Monster.Types.Version('1.0.0').toString();
</script>
</body>
</html>
```
Besides the CDN [jsdelivr](https://www.jsdelivr.com/package/npm/@schukai/monster),
[skypack](https://cdn.skypack.dev/@schukai/monster@latest),
Monster can also be obtained via [NPM](https://www.npmjs.com/package/@schukai/monster)
or the [git repos](https://gitlab.schukai.com/oss/libraries/javascript/monster).
Monster itself has no dependencies and does not dictate anything to you,
you can use Monster with Bootstrap, jQuery or other cool frameworks.
Monster is a lightweight, robust and easy-to-use library with modest ambitions.
Monster integrates easily with your existing websites without taking over everything.
Here's ({@tutorial what-is-monster}) what Monster is all about. In section {@tutorial getting-started}
you can read how to integrate Monster into your own web projects.
Monster has a free license and therefore you are free to customize Monster to your needs.
Monster is a collection of functions and classes that can help in the daily work
with Javascript to get faster to the goal. Monster does not require you to be the
only library, nor does it require you to use only Monster.
Monster itself has no dependencies and works perfectly with other frameworks
like jQuery or Bootstrap.
**The design goals of Monster's core library are:**
* Easy integration with existing user interfaces.
* Robust interfaces
* Tested code and good code coverage.
* No dependencies on other libraries
\ No newline at end of file
The DOM classes, functions and constants help to perform various tasks in the browser.
Among other things, the [form components](https://monsterjs.org/en/doc/components/form/latest/) were
developed on the basis of this functionality.
|
The localization classes and functions support you in dealing with
translations and country-specific settings.
\ No newline at end of file
In the monster library, we use current functions and objects for the implementation.
Some functions may not run on older runtimes.
There are several ways to increase compatibility.
One way is [Babel](https://babeljs.io/) and the `@babel/preset-env ` preset.
More information about the browsers supported by Babel can be found in the [browserslist](https://github.com/browserslist/browserslist) project.
## Polyfills
A polyfill can be assembled using the [create-polyfill-service-url](https://www.npmjs.com/package/create-polyfill-service-url) tool.
@startuml
autonumber
Script -> DOM: element = document.createElement('my-element')
create CustomElement
DOM -> CustomElement: constructor()
activate CustomElement
CustomElement -> CustomElement: [initMethodSymbol]()
create ProxyObserver
CustomElement -> ProxyObserver: new
activate CustomElement
CustomElement -> CustomElement: [initMethodSymbol]()
CustomElement --> DOM: Element
DOM --> Script : element
Script -> DOM: document.querySelector('body').append(element)
DOM -> CustomElement : connectedCallback()
note right CustomElement: is only called at\nthe first connection
CustomElement -> CustomElement : [assembleMethodSymbol]()
activate CustomElement
CustomElement -> CustomElement: initShadowRoot()
activate CustomElement
CustomElement -> CustomElement: attachShadow()
CustomElement -> CustomElement: appendChild()
CustomElement --> CustomElement
deactivate CustomElement
CustomElement -> CustomElement: initCSSStylesheet()
create UpdaterSet
CustomElement -> UpdaterSet : new
loop every element
create Updater
CustomElement -> Updater : new
CustomElement -> UpdaterSet:add(Updater)
CustomElement -> Updater : run
end
CustomElement -> CustomElement: addObjectWithUpdaterToElement()
activate CustomElement
create MutationObserver
CustomElement -> MutationObserver: new
activate MutationObserver
MutationObserver -> MutationObserver : observe
MutationObserver --> CustomElement
deactivate CustomElement
deactivate CustomElement
deactivate CustomElement
deactivate CustomElement
deactivate CustomElement
... ...
autonumber
Script -> DOM: document.querySelector('monster-confirm-button').parentNode.removeChild(element)
DOM -> CustomElement: disconnectedCallback()
@enduml
\ No newline at end of file
Monster's updater uses a DOM-based approach. The configuration and the template system are valid and parsable HTML.
The configuration is done via some special attributes with a `data-monster-` prefix.
Code is always the most informative. So let's take a look at a complete example right away.
```
// The first thing to do is to include the Updater class.
import {Updater} from 'https://cdn.skypack.dev/@schukai/monster@latest/source/dom/updater.js';
// Now we prepare the html document.
// This is done here via script, but can also be inserted into the document as pure html.
// To do this, simply insert the tag <h1 data-monster-replace="path:headline"></h1>.
const body = document.querySelector('body');
const headline = document.createElement('h1');
headline.setAttribute('data-monster-replace','path:headline')
body.appendChild(headline);
// the data structure
let obj = {
headline: "Go!",
};
// Now comes the real magic. we pass the updater the parent HTMLElement
// and the desired data structure.
const updater = new Updater(body, obj);
// now we get the used data structure. why can't we take the original structure?
// the updater puts a proxy over the data structure and thus allows to monitor changes.
// We would not see changes to the original object.
const subject = updater.getSubject();
// now start the updater
updater.run();
// Now you can change the data structure and the HTML will follow these changes.
// to illustrate, let's put the change into a timer call.
setTimeout(function(){
console.log(obj);
subject['headline'] = "Hello!"
},1000);
```
We have seen how we can change the content of an htm element. now let's look at what options are available.
## Replace
The simplest manipulation is to replace the content of a HTMLElement.
To do this, simply use the `data-monster-replace` attribute (see example).
The syntax is quite simple. The result of the attribute pipe is inserted as content of the
HTMLElement. For the processing the [Pipe](Monster_Data.Pipe.html)<!-- @IGNORE PREVIOUS: link --> and [Transformer class](Monster_Data.Transformer.html)<!-- @IGNORE PREVIOUS: link -->
is used.
If, for example, you have an object `x` with the structure listed below and want to insert the value of the key b, you write: `path:a.b`.
The pipe can then be used to apply operators. For example, `tolower` can be used to convert everything to lowercase.
```
let x = {
a: {
b: "EXAMPLE"
}
}
```
this is how it looks as an attribute:
```
<div data-monster-replace="static:HELLO | tolower"></div>
```
The result is then the following html:
```
<div data-monster-replace="static:hello">hello</div>
```
A full example looks like this:
```
import {Updater} from 'https://cdn.skypack.dev/@schukai/monster@latest/source/dom/updater.js';
const body = document.querySelector('body');
const headline = document.createElement('h1');
headline.setAttribute('data-monster-replace','static:hello')
body.appendChild(headline);
// result in ↦ <div data-monster-replace="static:hello"></div>
new Updater(body).run();
```
## Attributes
Attributes can be added via the `data-monster-attributes` attribute. The syntax is attribute name followed by
a space and the desired pipe definition.
```
<div data-monster-attributes="id static:myid">hello</div>
```
The result is then the following html:
```
<div id="myid" data-monster-attributes="id static:myid">hello</div>
```
Multiple attributes can be separated by commas.
```
<div data-monster-attributes="id static:myid, class static:myclass">hello</div>
```
## Remove
The `data-monster-remove` attribute can be used to remove html elements. it is important to
note that this cannot be undone. Once removed, nodes will not be reinserted.
This tag is removed via the updater
```
<div data-monster-remove></div>
```
## Insert
The strongest feature is adding elements to a node.
For this feature you need a template and the `data-monster-insert` attribute.
The syntax of the attribute is first an id followed by a space. This is then followed by the pipe command.
```
<ol data-monster-insert="myid path:a"></ol>
```
Furthermore, you need a template. The template must have the same string as id.
```
<template id="myid">
<li data-monster-replace="path:myid | index:id | tostring | prefix:x"></li>
</template>
```
In this template, we define the structure of the new elements. In this case, the id is taken
from the dataset `index:id`, converted to a string `tostring`, and an x is placed in front of it `prefix:x`.
The values for the corresponding data must be available as an array.
```
let obj = {
a: [
{"id": 1},
{"id": 2},
{"id": 3},
{"id": 4}
]
};
```
Below we have a complete example. Instead of specifying the template `<template />` in HTML, it is constructed via
javascript `document.createElement('template')`. But it is essentially the same.
```
const body = document.querySelector('body');
const li = document.createElement('li');
li.innerHTML="-/-";
li.setAttribute('data-monster-replace','path:myid | index:id | tostring | prefix:x');
const template = document.createElement('template');
template.setAttribute('id','myid');
template.content.appendChild(li);
body.appendChild(template);
const list = document.createElement('ul');
list.setAttribute('data-monster-insert', 'myid path:a')
body.appendChild(list);
let obj = {
a: [
{"id": 1},
{"id": 2},
{"id": 3},
{"id": 4}
]
};
const updater = new Updater(body, obj)
updater.run();
```
The result will be
```
<ul data-monster-insert="myid path:a">
<li data-monster-replace="path:a.0 | index:id | tostring | prefix:x" data-monster-insert-reference="myid-0">x1</li>
<li data-monster-replace="path:a.1 | index:id | tostring | prefix:x" data-monster-insert-reference="myid-1">x2</li>
<li data-monster-replace="path:a.2 | index:id | tostring | prefix:x" data-monster-insert-reference="myid-2">x3</li>
<li data-monster-replace="path:a.3 | index:id | tostring | prefix:x" data-monster-insert-reference="myid-3">x4</li>
</ul>
```
You can easily add and delete values to the array. The DOM will be adjusted accordingly.
The attribute `data-monster-insert-reference` identifies if the entry already exists.
Forms can be bound to a structure just like other structures via the updater class.
Suppose we have an html form with some controls.
```
<div>
<form id="form1">
<input type="checkbox" name="checkbox">
<input type="text" name="text">
<input type="radio" name="radio">
<select name="select">
<option>value1</option>
<option>value2</option>
</select>
<textarea name="textarea">
</textarea>
<input type="button" name="button">
</form>
</div>
```
A new updater is created in this case via the following call.
```
const updater = new Updater(document.getElementById('form1'));
```
With the method `Updater.getSubject()` you can get the structure of the updater. If you want to use an already defined structure, you can pass it to the updater as a second parameter.
```
let subject = updater.getSubject();
console.log(subject);
```
Now we want a click on the checkbox to be mapped in the data structure.
To do this we need to extend the html with the `data-monster-bind` attribute.
The values or states of controls are mapped to the data structure via this binding.
```
<div>
<form id="form1">
<!-- data-monster-bind added -->
<input type="checkbox" name="checkbox" data-monster-bind="path:state">
</form>
</div>
```
In this case the status of the checkbox is mapped to the key `state`. If the checkbox is selected the field `state` contains the value `on` , otherwise state is undefined.
If you want to use another value instead of `on`, you can set the attribute `value`.
```
<input type="checkbox" name="checkbox" value="checked" data-monster-bind="path:state">
```
To see the magic the handling must still be switched on.
```
updater.enableEventProcessing()
```
Voila!
<p>
On this page you will find a sample implementation for many HTML5 controls. The binding is set up in both
directions, so that a change to the control also causes a change to the values, and changes to the values cause a change to the
control.
</p>
<p>
You can save the updater as a temporary variable in the console. In our case Chrome assigns the variable <code>temp1</code> to
the object.
</p>
<p>
Note that instead of <code>temp1</code> you have to take the variable from your browser.
</p>
<p>
And then access the data array using <code>temp1.getSubject().</code>
</p>
<p>
For example, to enable the checkbox you can set <code>temp1.getSubject().value.checkbox="YES"</code>. To deactivate the checkbox
you have to remove the value <code>temp1.getSubject().value.checkbox=undefined</code>
</p>
<p>
Note that you have to take the variable from your browser instead of temp1.
</p>
<p>
At the bottom you will find the JSON with all values
</p>
<h3>Examples</h3>
<template id="example1">
<style>
[readonly] {
background-color: #e9e9e9;
border: 1px solid grey;
}
div {
box-sizing: border-box;
}
.table {
display: grid;
grid-template-columns: 1fr 1fr 4fr;
}
.cell.control {
padding: 5px 20px;
}
.cell.value {
padding: 5px 20px;
}
.json {
margin: 5px 20px;
padding: 20px;
border: 4px solid #424242;
margin-top: 20px
}
</style>
<div id="container">
<div class="table">
<div class="cell control"><select type="input" data-monster-bind="path:value.multiple" multiple>
<option value="a" data-monster-attributes="selected path:value.multiple | call:checkstate">A
</option>
<option value="b" data-monster-attributes="selected path:value.multiple | call:checkstate">B
</option>
<option value="c" data-monster-attributes="selected path:value.multiple | call:checkstate">C
</option>
</select></div>
<div class="cell value"><input readonly data-monster-attributes="value path:value.multiple"></div>
<div class="cell code"></div>
<div class="cell control"><select type="input" data-monster-bind="path:value.select"
data-monster-attributes="value path:value.select">
<option value="a">A</option>
<option value="b">B</option>
<option value="c">C</option>
</select></div>
<div class="cell value"><input readonly data-monster-attributes="value path:value.select"></div>
<div class="cell code"></div>
<div class="cell control"><input type="checkbox" data-monster-bind="path:value.checkbox" value="YES"
data-monster-attributes="checked path:value.checkbox | call:checkstate">
</div>
<div class="cell value"><input id="my" readonly data-monster-attributes="value path:value.checkbox"></div>
<div class="cell code"></div>
<div class="cell control"><input type="search" name="search" data-monster-bind="path:value.search"
data-monster-attributes="value path:value.search"></div>
<div class="cell value"><input readonly data-monster-attributes="value path:value.search"></div>
<div class="cell code"></div>
<div class="cell control"><input type="tel" name="tel" data-monster-bind="path:value.tel"
data-monster-attributes="value path:value.tel"></div>
<div class="cell value"><input readonly data-monster-attributes="value path:value.tel"></div>
<div class="cell code"></div>
<div class="cell control"><input type="time" name="time" data-monster-bind="path:value.time"
data-monster-attributes="value path:value.time"></div>
<div class="cell value"><input readonly data-monster-attributes="value path:value.time"></div>
<div class="cell code"></div>
<div class="cell control"><input type="url" name="url" data-monster-bind="path:value.url"
data-monster-attributes="value path:value.url"></div>
<div class="cell value"><input readonly data-monster-attributes="value path:value.url"></div>
<div class="cell code"></div>
<div class="cell control"><input type="week" name="week" data-monster-bind="path:value.week"
data-monster-attributes="value path:value.week"></div>
<div class="cell value"><input readonly data-monster-attributes="value path:value.week"></div>
<div class="cell code"></div>
<div class="cell control"><input type="color" name="color" data-monster-bind="path:value.color"
data-monster-attributes="value path:value.color"></div>
<div class="cell value"><input readonly data-monster-attributes="value path:value.color"></div>
<div class="cell code"></div>
<div class="cell control"><input type="radio" name="doit" data-monster-bind="path:value.radio" value="2"
data-monster-attributes="checked path:value.radio | call:checkstate"><br>
<input type="radio" name="doit" data-monster-bind="path:value.radio" value="1"
data-monster-attributes="checked path:value.radio | call:checkstate"></div>
<div class="cell value"><input readonly data-monster-attributes="value path:value.radio"></div>
<div class="cell code"></div>
<div class="cell control"><input type="range" name="range" data-monster-bind="path:value.range"
data-monster-attributes="value path:value.range"></div>
<div class="cell value"><input readonly data-monster-attributes="value path:value.range"></div>
<div class="cell code"></div>
<div class="cell control"><input type="password" name="password" data-monster-bind="path:value.password"
data-monster-attributes="value path:value.password"></div>
<div class="cell value"><input readonly data-monster-attributes="value path:value.password"></div>
<div class="cell code"></div>
<div class="cell control">
<input type="number" name="number" data-monster-bind="path:value.number"
data-monster-attributes="value path:value.number"></div>
<div class="cell value"><input readonly data-monster-attributes="value path:value.number"></div>
<div class="cell code"></div>
<div class="cell control">
<input type="month" name="month" data-monster-bind="path:value.month"
data-monster-attributes="value path:value.month"></div>
<div class="cell value"><input readonly data-monster-attributes="value path:value.month"></div>
<div class="cell code"></div>
<div class="cell control"><input type="email" name="email" data-monster-bind="path:value.email"
data-monster-attributes="value path:value.email"></div>
<div class="cell value"><input readonly data-monster-attributes="value path:value.email"></div>
<div class="cell code"></div>
<div class="cell control"><input type="datetime-local" name="datetime-local"
data-monster-bind="path:value.datetime-local"
data-monster-attributes="value path:value.datetime-local"></div>
<div class="cell value"><input readonly data-monster-attributes="value path:value.datetime-local"></div>
<div class="cell code"></div>
<div class="cell control">
<input type="date" name="date" data-monster-bind="path:value.date"
data-monster-attributes="value path:value.date"></div>
<div class="cell value"><input readonly data-monster-attributes="value path:value.date"></div>
<div class="cell code"></div>
</div>
<div class="json"
data-monster-replace="path:value | tojson"></div>
</div>
</template>
<script src="https://monsterjs.org/vendor/prism.js"></script>
<link href="https://monsterjs.org/vendor/prism-coy.css" rel="stylesheet"/>
<script type="module">
'use strict';
import {Updater} from "https://cdn.skypack.dev/@schukai/monster@latest/source/dom/updater.js";
document.addEventListener('readystatechange', () => {
if (document.readyState == 'complete') {
let included = false;
let example1 = document.getElementById('example1');
let article = document.querySelector('article');
if (article) {
article.appendChild(example1.content);
included = true;
} else {
// for local debug
document.body.appendChild(example1.content);
}
document.querySelectorAll('div[class*=control]').forEach((element) => {
let col = element.nextElementSibling.nextElementSibling;
let source = (element.innerHTML);
let pre = document.createElement('pre');
pre.setAttribute('class', 'prettyprint source')
let code = document.createElement('code');
col.appendChild(pre);
pre.appendChild(code);
source = Prism.highlight(source, Prism.languages.html, 'html');
code.innerHTML = source
})
const container = document.getElementById('container');
const updater = new Updater(container, {
value: {
input: "Init Value - input",
textarea: "Init Value - textarea",
file: "Init Value - file",
checkbox: "Init Value - checkbox",
radio: "Init Value - radio",
multiple: ['a', 'c']
}
});
updater.run();
updater.enableEventProcessing();
console.log(updater);
}
});
</script>
In diesem Artikel geht es um das Erstellen einer eigenen
[Web Komponente](https://developer.mozilla.org/en-US/docs/Web/Web_Components).
Mit Hilfe von Klassen und Funktionen von [Monster](https://monsterjs.org)
Zuerst erstellen wir eine von `CustomControl` abgeleitete Klasse. Die Elternklasse
`CustomControl` implementiert bereits einige Funktionen, die das Leben im folgenden
leichter machen.
```
class Button extends CustomControl {
}
```
Der nächste Schritt legt fest, welchen Tag das Control im HTML bekommen soll.
```
class Button extends CustomControl {
static getTag() {
return "monster-button";
}
}
```
Damit man später das Control konfigurieren kann, müssen wir die Möglichkeit,
Optionen anlegen zu können, schaffen. Wir können hier auf die Strukturen der Klasse
`CustomControl` aufsetzen und brauchen nur ein eigenes Property `default` anzulegen.
```
class Button extends CustomControl {
// ..... other implementations
get defaults() {
return Object.assign({}, super.defaults, {})
}
}
```
Über diese Struktur werden auch die Templates des Controls definiert. So kann man
dem Control ein Standard mitgeben und dem Anwender des Controls die Möglichkeit geben
die Templates anzupassen.
Jetzt machen wir uns also an das Aussehen des Controls und erstellen dazu ein Template.
Wir wollen das der Button einen einfachen HTML5-Button verwendet.
```
<button>Hello!</button>
```
Das Haupttemplate des Buttons wird über die Option `templates.main` gepflegt.
Wir müssen das HTML also in die oben eingeführte `default` Struktur einfügen. Wir können
das HTML direkt hier angeben.
```
return Object.assign({}, super.defaults, {
templates: {
main: `<button>Hello!</button>`
},
})
```
Um die Übersicht bei größerne Templates zu behalten, kann man alternativ das
Template auch in eine Funktion auslagern und hier nur die Funktion aufrufen.
```
return Object.assign({}, super.defaults, {
templates: {
main: getTemplate()
},
})
```
Wenn wir jetzt den HTML-Tag `<monster-button></monster-button>` in eine HTML-Datei einfügen
erhalten wir soweit so unspektakulär einen Button.
![Screenshot](images/button1.png)
Jetzt kommt die Magie. Im DOM gibt es zwei wichtige Methoden, die beim Einbinden und
beim Entfernen eines Controls aufgerufen werden.
Wird ein Control in das DOM eingehängt, so wird die Methode
[`connectedCallback`](https://developer.mozilla.org/de/docs/Web/Web_Components/Using_custom_elements#using_the_lifecycle_callbacks) aufgerufen.
Beim Entfernen eines Controls aus dem DOM wird dagegen die Methode
[`disconnectedCallback`](https://developer.mozilla.org/de/docs/Web/Web_Components/Using_custom_elements#using_the_lifecycle_callbacks) aufgerufen.
aufgerufen.
Wir implementieren die beiden Methoden in unserer neuen Klasse.
```
class Button extends CustomControl {
// ..... other implementations
connectedCallback() {
super.connectedCallback();
}
disconnectedCallback() {
super.disconnectedCallback();
}
}
```
Innerhalb dieser beiden Methoden können wir nun zum Beispiel Strukturen initailisieren oder
Eventhandler hinzufügen und wieder entfernen.
Das `CustomControl` besitzt zwei weitere wichtige Methoden, die
man überschreiben kann, um das Control zu initialisieren. Diese beiden Methoden
haben keinen direkten Methodennamen, sondern verbergen sich hinter einem
Symbol-Schlüssel.
```
import {
assembleMethodSymbol,
initMethodSymbol,
} from "@schukai/monster/dist/modules/dom/customelement.js";
class Button extends CustomControl {
// ..... other implementations
[initMethodSymbol]() {
super[initMethodSymbol]();
}
[assembleMethodSymbol]() {
super[assembleMethodSymbol]();
}
}
```
Die Methode `[initMethodSymbol]()` wird direkt vom Konstruktor aufgerufen und dient zur einmaliegen initialisierung
interner Strukturen.
Die Methode `[assembleMethodSymbol]()` wird beim ersten einbinden des Kontrols in das
DOM aufgerufen. Wird das Control wieder entfernt und erneut eingebunden, so wird
`[assembleMethodSymbol]()` nicht nochmal aufgerufen.
@startuml filename2.png
Alice --> Bob
@enduml
We need the `Formatter` and `Translations` class and the `parseLocale()` function to handle translations.
In the context of the DOM we can also use the `getLocaleOfDocument()` method.
```
import {Translations} from 'https://cdn.skypack.dev/@schukai/monster@latest/source/i18n/translations.js';
import {Formatter} from 'https://cdn.skypack.dev/@schukai/monster@latest/source/text/formatter.js';
import {parseLocale} from 'https://cdn.skypack.dev/@schukai/monster@latest/source/i18n/locale.js';
import {getLocaleOfDocument} from 'https://cdn.skypack.dev/@schukai/monster@latest/source/dom/locale.js';
```
Let's start with the function `parseLocale()`. This function can create a `Locale` object from a string.
So we can create the corresponding `Locale` object from the string `en_GB`.
```
const locale = parseLocale('en_GB')
// ↦ Locale {}
```
If we move in the browser, so we can also use the function `getLocaleOfDocument()`.
This function returns a locale object as a result.
This function looks if the HTML tag `<html lang="en">` has a lang attribute.
If no locale is defined, the default value is assumed to be `en`.
We now need an object with the translations. For this we use the `Translations` class.
We can either define the translations ourselves or load them via an API.
```
// define translations
const translation = new Translations(parseLocale('en-GB'));
translation.assignTranslations({
text1: 'hello world!',
text2: {
'one': 'click once',
'other': 'click ${n | tostring} times' // this is where the pipe comes in
}
});
// fetch from API
const translation = new Fetch('https://example.com/${language}.json').getTranslation('en-GB');
// ↦ https://example.com/en.json
```
If we now have a translation, we can now read the desired strings. For this
we use the method `Translation.getText()` or the method `Translation.getPluralRuleText()`
for texts with more than one number.
```
const message = translation.getText('text1');
// -> hello world
```
To translate texts with number references, enter the desired number.
```
let n=1;
const message1 = translation.getPluralRuleText('text2', n);
// -> click once
n=2
const message2 = translation.getPluralRuleText('text2', n);
// -> click ${n} times
```
To replace the placeholder now, the formatter is used.
```
const text = new Formatter({n}).format(message2);
console.log(text)
// ↦ click 2 times
```
Finally, let's take a look at the formatter class from the i18n module.
```
import {Formatter} from
'https://cdn.skypack.dev/@schukai/monster@latest/source/i18n/formatter.js';
```
A tranlate object can be passed directly to this class.
There is also the possibility to pass values in the format string.
```
// define translation
const translations = new Translations('en')
.assignTranslations({
// text with placeholder
message: "${animal} has eaten the ${food}!"
});
// without marker and inline values
new Formatter({}, translations).format("message::animal=dog::food=cake")
// ↦ dog has eaten the cake!
```
Voila!
documentation/tutorial/images/button1.png

717 B

{ {
"01-getting-started": {
"title": "Getting Started"
},
"02-what-is-monster": {
"title": "What is Monster?"
},
"03-dom": {
"title": "DOM",
"children": {
"dom-form-handling": {
"title": "Form handling"
},
"form-example": {
"title": "Form examples"
},
"dom-based-templating-implementation": {
"title": "DOM-based templating implementation"
},
"how-to-write-a-customcontrol": {
"title": "How to write a CustomControl"
}
}
},
"04-i18n": {
"title": "Internationalization",
"children": {
"i18n-locale-and-formatter": {
"title": "Locale & Formatter"
}
}
},
"09-browser-compatibility": {
"title": "Browser compatibility"
}
} }
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment