MakiStat is an application and a template-based framework designed for generating small yet expanding multilingual websites. It requires relatively minimal technical knowledge and does not require any initial configuration to run it.
Warning
This documentation covers the beta version of the MakiStat and its features may be changed in the future.
MakiStat is a static site generator designed for developing small-scale websites in multiple languages and using data occuring in multiple places. MakiStat uses HTML templates powered by Scriban template engine which provides full-fledged programming language (see this repository for more info). Thanks to it, templates can be written like HTML with embedded back-end code but the output pages have fixed content and cannot be randomized unless the JavaScript is included. MakiStat fills pages with data stored in JSON files.
MakiStat is also a framework providing separation the information exclusive to particular
pages and information used in multiple pages. In general, it supports the structure consisting
of the skeleton.html file which is the main template used for all the pages on the website and
_global and _main folders which store templates and the data. MakiStat generates pages in the
predefined output folder which has to be in the same folder as skeleton and the website in
the default language is placed directly in the output folder. Language versions are placed in subfolders
and their structure is generally the same as the default language website.
MakiStat cannot directly handle non-HTML resources (such as CSS, scripts and images)
and you need to store them in the output folder and maintain them separately,
using already generated website.
MakiStat can be used for websites which are expected to be slightly expanded in the future and use relatively little data of the same kind they would be included in database, where maintenance of the database and the CMS would be too cumbersome and expensive. Data can be stored in JSON files and used by multiple pages, although it is up to the author how they should be organized. It can be also used for making mock-ups of larger-scale websites before designing and deploying the database and other complex web technologies.
MakiStat application itself is very easy to use since it is an ordinary desktop application and once the structure of the website and the data have been already created, the website can be generated in two simple steps.
Writing the structure itself require only the basics of programming and JSON syntax and you do not need to know any other frameworks. MakiStat does not give any ready layout which you have to modify for your needs what would be difficult when your layout needs to be very different from the provided.
Generated website can be manually published to the traditional hosting services.
The data and the templates need to be organized in the two folders: _global and _main.
Only files which can be outside are the skeleton.html which store the main layout of all of the pages
and .json files of the same name which contain elementary data (such as strings and numbers)
meant to be put to the skeleton template.
.
├── _global # Resources used across entire website
├── _main # Resources related to output pages
├── output # Folder for output website
├── _skeleton.html
└── _skeleton.default.json
If you want to learn on how to write your first website for MakiStat, go to tutorial section.
Folder intended to store templates and data used in multiple pages and not related to the specific page.
Folder indented to store website source files which directly represent specific pages. Page
templates can read partials and data from both _global and _main folders, although it is a good
practice to put the data not being a part of the specific page to be put in _global folder.
The folder structure in this folder is mostly mirrored in the output folder. MakiStat seeks page templates
only in this folder.
Folder intended to be destination point of the website. The output has almost exactly the same folder
structure as _main folder. Only difference is the presence of folders named after language codes which
store the same website in different languages.
There are several types of file making up the "blueprint" of the final static website.
Templates contain HTML and Scriban code. Scriban provides full-featured programming language
which can be used for embedding PHP or Razor-like scripts providing non-standard ways to handle
content meant to be put in output pages. MakiStat template engine can, besides putting data itself,
read data from other JSON files and render the content as it would be preprocessed with
back-end code by a web server. They must always have the .html extension.
This is the the skeleton layout template of every page in the website. It contains HTML elements used in every
page and has references to the sections written in page templates. MakiStat inserts the processed page
sections in place of these references. Simple language-variable data are read from
language-variable json files of the same name. There can be exactly one skeleton in whole structure,
needs to have the name _skeleton.html and must be placed in the same folder as
three framework folders.
This is also the entry point for MakiStat from where it starts the generation of the website.
Template representing the actual page expected to be in the output. They can be put only in _main folder,
since MakiStat seeks for these templates only in that folder. Its name must not begin with _ character,
since MakiStat recognizes such files as partials and does not process them like the pages.
If it is placed in the subfolder chain, MakiStat generates the same subfolder chain and puts the page as
is placed in _main folder. The name of the page is also the same as its page template.
For example, MakiStat generates page _main/blogs/kitchen.html to the destination
output/blogs/kitchen.html.
Pages are divided into multiple user-defined sections put to the different places in the skeleton.
This is only template type the data from language version independent JSON files are inserted to, since the single page can have the same data, regardless of the language version but not the same as in others, such as image paths.
Partial is a template for the part of the page and cannot be used as standalone page template itself.
Its name must begin from underscore character to be skipped by MakiStat during website generation and not
treated as the output page. The good practice is to place partials in the _global folder,
since they are not intended to be the part of only one page.
Data are saved in JSON files and loaded to the pages by the MakiStat. They represent simple and sometimes
translateable data which differ across pages and their language versions.
They must always have .json extension and have the same name as the template if they are
associated with and placed in the same folder e.g. language-independent data loaded to the
page kitchen.html need to have name kitchen.json.
Data which always are the same in all of the language versions of the same page but still specific to
that one. They have simple .json extension. Data meant to be stored in such files are file paths,
numbers, URLs, data used for scripts etc. They can be used only with page templates.
Data containing data specific for the given language version. Unlike language-independent data, they
can be put to the all of the types of template. Each template needs to have at least one language version
variable data file and needs to have [lang code].json compound extension, where lang code
is the language code. It can have predefined default value if the language version is meant to be
put to the root of the website or the user-defined code associated with specific language version. For example,
the data for index.html page in default language need to be defined in kitchen.default.json
and has to be in the same folder. However, if there is index.pl.json file in the same folder, the
same page in different language version will be generated to output/pl/index.html.
Presence of language JSON files for skeleton template determines what language versions will be generated.
For example, if there are some pl.[lang_code].json in the _main folder, but the
_skeleton.pl.json file is absent, language version with pl code will not be generated.
If the version language JSON file for skeleton is available but some web page does not have equivalent
template, the page in that specific language version will not be generated e.g. when the
_skeleton.pl.json file exists, but the exclusive.pl.json does not, the exclusive.html
page in version associated with pl language code will not be generated and the right warning will
be printed in website.log file placed in the root of the website blueprint.
Data files not associated with specific template. They are meant to store information used by multiple
templates and can be loaded using function load_model. They can be named in any way, although it is
recommended to be placed in the _global folder.
Most features of the MakiStat API are provided by the Scriban template engine which is a third-party API and its documentation is available at https://github.com/scriban/scriban#documentation . That is why this section covers API features exclusive to MakiStat. They are defined using tools provided by Scriban.
Object containing data read from skeleton data file associated with processed language version.
Object containing data read from the language-independent data file of the same name as the page template. This object is scoped to the page and the skeleton.
Object containing data read from the language variable data file of the same template. This object is scoped to the page and the skeleton.
Name of the currently processed page, without extension.
Current lang code. Its type is string.
Directory of the language version of the website. If the language code has value of default,
its the root of the website.
Absolute path to the _main folder.
Absolute path to the _global folder.
Object containing language-independed data passed to the load_partial function.
This object is scoped to the partial.
Object containing language-variable data passed to the load_partial function.
This object is scoped to the partial.
Object containing language-variable data exclusive to the partial and read from the file of the same name and placement. This object is scoped to the partial.
It processes the page template and renders its section of the name passed through parameter
section_name. It must be called only on skeleton template.
template_name: string containing path to the page template..htmlextension is added automatically.section_name: string containing section declared in the page template.
Returns: processed section of the page.
It processes the partial and renders it. It accepts two objects with language-independent and language-variable data.
template_name: string containing path to the partial template. .html extension is added automatically.model: object containing language-variable data to be passed to partialuni_model: object containing language independent data to be passed to partial
Returns: processed partial.
Loads data from a JSON file. Depending on a value of multilingual parameter, it reads
language-variable or language independent data.
Language is determined by language code of currently processed language.
model_path: string containing path to the JSON file. The name itself should not even contain language code from the language-variable data JSON extension because of the second argument.multilingual: boolean determining if the data must be read from language-variable or language independent data.
Returns: object containing data read from JSON file.
Returns: array containing available language codes. They are useful when you generate list of the language versions of the current page.
This function returns path to the processed page in language version associated with given language code. It can be useful for generating paths in the list of the same page in available languages.
lang_code: language code needed to determine the path of the processed page.
Returns: path to the page in language version associated with given language code.
First, it reads the skeleton.html file which is the starting point for MakiStat. For each skeleton language data file,
it downloads global data specific for the language of code saved in the file extension.
For each page which is not generated, to which one of the needed resources have been modified
before or is not registered in check.msmc, the page is generated.
MakiStat recognizes html files as page templates as long as their names do not begin
with underscore. First, the data from the page are inserted into the skeleton.
After that, data from partials and JSON files referred in the page templates and
the partials are processed and put to the page in the memory. If the page processing is successful,
its contents are saved to the file. If some error occurs, entire generation process is stopped.
This tutorial is written for the MakiStat beginners but already familiar with HTML and Scriban language.
First, choose your folder where you want to create wesbite blueprint for MakiStat.
Nextly, create three folders: _global, _main and output.
Create _skeleton.html in the same folder. Fill it with this code:
<!DOCTYPE html>
<html lang="{{ if lang_code == 'default'
'en'
else
lang_code
end }}">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ page.title }} - Great Cooking</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=DynaPuff:wght@400;600&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/style.css">
{{- load_page page_file 'stylesheets' ~}}
</head>
<body>
<div id="main">
<header>Great Cooking</header>
<main>
{{- load_page page_file 'main' ~}}
</main>
<footer>
<p style="margin-bottom: 0.4em;">{{ global.copyright }}</p>
<div class="lang-menu">
<ul>
<li><a href="{{ load_lang_page_url 'pl' }}" >PL</a></li>
<li><a href="{{ load_lang_page_url 'default' }}" >EN</a></li>
</ul>
</div>
</footer>
</div>
</body>
</html>Code fragments between double curly braces and the braces themselves will be replaced by the content in the output page. The content can be read from data saved in JSON files or entire web page fragment processed from child templates. Skeleton nests sections saved in page templates and the page templates themselves can nests partials.
After MakiStat read the skeleton, it reads and processes each page template which needs to be
in _main folder and its name cannot begin with underscore, since templates with such names
are recognized as partials. For more information, how the pages are processed,
see this chapter.
Code written as value of lang attribute is replaced by MakiStat with the value of
lang_code which stores current language code which was read from the first member of
compound extension of one of _skeleton.[lang code].json files which store language-dependent
data scoped to all templates in the project. If its value is default, MakiStat puts en
value here, since in this tutorial, English is default language of the website and the version in such
language will be put in the root folder. Other language versions are put in subfolders whose name will
be exactly the same as their language codes.
page.title is replaced by the data saved in title property in the page global
object which store data exclusive for both the page and the language version.
page data file with the
currently processed language. Language is determined by language code read from the compound extension
of the JSON file named _skeleton.
Source line of code load_page page_file 'main' is actually a function call - load_page
is a predefined function name and page_file and 'main' are parameters.
There are no parentheses and commas, since Scriban template engine uses language based on Ruby which
does not use such symbols in function calls.
load_page actually puts the section saved in currently processed web page. page_file is
predefined variable which stores the path of the processed page. Second parameter of the load_page
is the name of the section defined in the page template.
global.copyright is replaced by the data saved in copyright property in the
global global object which store information different across the language versions but
always the same in all pages of the specific language version.
load_lang_page_url 'pl' call renders the URL leading to the same page in Polish language
whose code is 'pl'. Similar call in line below renders the same URL in English language.
Number of such files determine number of language versions of the same website and one of them always
must have default lang code.
In the tutorial, we will create bilingual website - in English and Polish language. Therefore, we need
to create two data files which will be referred by the skeleton template and therefore, will be on every
page. However, they are different across language versions. Give them names _skeleton.default.json
and _skeleton.pl.json. Fill the first one with this code:
{
"copyright": "All Rights Reserved"
}and the second one with:
{
"copyright": "Wszelkie Prawa Zastrzeżone"
}default code means that the language version of the website will be stored in website root.
The default language defined in this tutorial is English. Polish language version will be stored
in pl subfolder, since language code of Polish version is pl.
Code in both files is very short, since we use only one multilingual entry which will be used in all pages. Language-variable data files should have generally the same set of properties, although additional or missing properties are acceptable as long as the template code will handle these non-standard cases.
Data from one of the _skeleton.[lang code].json files is written to the global global
variable by MakiStat. The order of languages the MakiStat processes depends of the order of file names read
from file system.
Create template of the index page in _main folder and name it index.html.
Fill it with this code:
{{~ if section == 'main' ~}}
<h1>{{ page.header }}</h1>
<p>{{ page.description }}</p>
<p>{{ page.suggestion }} <a href="recipes.html">{{ page.link }}</a></p>
{{~ end ~}}First line of source code declares main section and the content before line containing end
will be rendered in place of {{- load_page page_file 'main' ~}} line code in _skeleton.html.
Sections are not mandatory and can be skipped, like stylesheet section in this example. When
section is not declared, MakiStat will leave empty place.
Create index.default.json file in the same folder as index.html page template file.
Fill it with code:
{
"title": "Home Page",
"header": "Welcome to Great Cooking!",
"description": "Here you find some quick'n'easy dish recipes!",
"suggestion": "For most popular recipes, ",
"link": "go here"
}There is no page.title reference in index.html template, so why this JSON
has title property has been included? Value assigned to title will be put to the
reference which you have added in _skeleton.html.
Many pages may contain components having the same HTML tag structure and they can be saved in
partial templates. Create recipes folder in _main folder and create three page templates:
fizzy.html, sandwich.html and orange_rice.html. Fill each of them with HTML code:
{{~ if section == 'stylesheets'}}
<link rel="stylesheet" href="/recipes/recipes.css">
{{~ end -}}
{{~ if section == 'main'
load_partial (global_path + '/_recipe') page uni_page
end ~}}Why has every file exactly the same content? It is because the page uses _recipe HTML partial
template which needs to be placed in _global folder whose path is stored in global_path
global variable. Partial is processed and loaded by load_partial function which passes three
parameters: path to the partial template, page global object and uni_page global object
which unlike page object, it stores information used in all language versions but still exclusive to
currently processed page. Data stored in these objects will be put to the partial.
These pages use additional stylesheet and that's why stylesheets section is included here.
Create _recipe.html partial template in the _global folder.
Although MakiStat does not seek this folder for the pages, it is still good to put trailing underscore
in the partial file name to avoid confusion with page templates. Fill this file with this content:
<h1>{{ model.header }}</h1>
<img src="/img/{{ uni_model.image }}.jpg" alt="{{ partial.illustration }}" loading="lazy">
<section>
<h2>{{ partial.ingredientsHeader }}</h2>
<ul class="ingredients">
{{ for $ingredient in model.ingredients ~}}
<li>{{ $ingredient }}</li>
{{~ end }}
</ul>
</section>
<section>
<h2>{{ partial.stepsHeader }}</h2>
<ol class="recipe">
{{ for $step in model.steps ~}}
<li>{{ $step }}</li>
{{~ end }}
</ol>
</section>Structure of template is similar to the page template, but it uses model and uni_model
instead of page and uni_page, respectively. These variables are two last parameters
of load_partial function which was called in parent template. load_partial passes
data from files which are mentioned below.
As with index.html, you need to create fizzy.default.json, sandwich.default.json
and orange_rice.html for each page template in recipes folder. Fill them:
fizzy.default.json:
{
"title": "Fizzy drink",
"header": "Fizzy drink from powder",
"ingredients":
[
"Water",
"Fizzy drink powder"
],
"steps":
[
"Fill the glass with water.",
"Warm the water in microwave oven for 30 seconds.",
"Put the powdered drink to the water and mix well."
]
}sandwich.default.json:
{
"title": "Sandwich with cheese and tomatoes",
"header": "Fizzy drink from powder",
"ingredients":
[
"Bread",
"Cheese",
"Tomatoes"
],
"steps":
[
"Slice bread, cheese and tomatoes.",
"Put the slices of cheese and tomato on bread and put the slice of bread on them.",
"Repeat two previous steps for remaining sandwiches."
]
}orange_rice.default.json:
{
"title": "Puffed Rice",
"header": "Puffed Rice with Orange Syrup",
"ingredients":
[
"Puffed rice",
"Orange syrup"
],
"steps":
[
"Put the puffed rice to the bowl.",
"Pour the syrup on the rice.",
"Mix the rice with the syrup well."
]
}However, they will not be
enough - data for these pages are passed to the partial coming from the same file and there is another
problem - these pages include image path which would be the same in all language versions. Writing the same
entry in each language variable data file would cause redundancy which would be particularly troublesome
when the website needs to be in many languages. And the path cannot be directly written in partial which
is loaded from single file but used by multiple page templates. That is why you need to create
fizzy.json, sandwich.json and orange_rice.json files which store information
which are language-independent but they still exclusive to the page, although they are meant to put to
the templates other than page templates MakiStat loads these data to them first. These data are loaded
to uni_page global object and they need to be passed to uni_model parameter. Fill them:
fizzy.json:
{
"image": "fizzy"
}sandwich.json:
{
"image": "sandwich"
}orange_rice.json:
{
"image": "rice"
}As you might have noticed, partials themselves may contain information which would be in multiple languages.
You need to create language-variable data files which are analogous to the data files read to page templates
i.e. they share the same main part of name and they are in the same folder as the partial. These data are
loaded to object partial which can be used only in partials.
Create file _recipe.default.json in the same folder as _partial and fill with code:
{
"illustration": "ilustracja",
"ingredientsHeader": "Ingredients",
"stepsHeader": "Steps"
}Create file recipes.html in _main folder and fill it with this code:
{{~ if section == 'main'}}
<h1>{{ page.title }}</h1>
<p>{{ page.description }}:</p>
<ul class="recipe-list">
{{ for $link in uni_page.links
$recipeData = load_model main_path + '/recipes/' + $link }}
<li><a href="recipes/{{ $link }}.html">{{ $recipeData.title }}</a></li>
{{ end }}
</ul>
{{~ end }}And create such data files:
recipe.default.json
{
"title": "Recipes",
"description": "There are all recipes available on the website"
}recipe.json
{
"links":
[
"fizzy",
"sandwich",
"orange_rice"
]
}Why there are recipe page template file names instead of titles in recipes.json?
Titles are already saved in the language data variable
files associated with recipe page templates and writing the same set of informations in multiple places
is too redundant. That is why recipes.html template calls load_model function in order
to read informations from recipes/fizzy.[current lang code].json,
recipes/sandwich.[current lang code].json and
recipes/orange_rice.[current lang code].json files and saves them to $recipeData
in each iteration. This is another situation, where language-independent data files come in handy - links
have the same value, no matter of the processed language and it might be changed in future.
These values can be directly embedded into template script but it would be not really convenient.
Thanks to load_model, you can use data JSON files as the database substitute, but without
automated consistency and validation.
All data files with Polish language content are stored in *.pl.json files.
They differ from *.default.json files only with language of the values and they are stored in the
same folder. You can copy them from demo which you can download from repository.
MakiStat is designed to handle HTML code only and therefore, you need to put stylesheets and scripts manually to the output. Fortunately, you can copy stylesheets from demo.
Now the website is ready for generation! Only things you need to do is to launch MakiStat, click Search
button, choose _skeleton.html file and click Generate! button. Your website is generated!
Note
The good news is that MakiStat is intelligent and does not generate pages when resources used for generation such pages are not changed.