Skip to content

Latest commit

 

History

History
411 lines (288 loc) · 19.4 KB

language.md

File metadata and controls

411 lines (288 loc) · 19.4 KB

glot: compositional web framework

Framework / Language

The GLOT language

GLOT is both a language and a framework for building websites. The name stands for Generative Language of Objects and Text. This section of the documentation describes the language.

The standard syntax of the language is the JavaScript Object Notation (JSON). The design of the language rests on two pillars: compositionality and multilingualism.

  • Compositionality is achieved by nesting JSON objects such that, at rendering time, they interpreted as the recursive instantiation rules of widget packages.

  • Multilingualism is achieved by enabling the use of localizable JSON objects when unilingual values are otherwise expected in HTML.

Elements

An element is defined as an unordered set of name/value properties of the form {"name": value, "name": value, ...}, such that each name/value pair matches one of the following constraints:

  • The name is tag and the value is a valid HTML tag;

  • The name is that of an HTML element attribute and the value's type is: localizable-value object, string, integer, or boolean;

  • The name is widget and the value matches the name of a widget package that defines a rendering rule, or is empty;

  • The name is params and the value is a JSON object that respects the parameter schema of the widget package set by the element's widget property;

  • The name is data and the value is: an element, a string, null, or an array with zero or more elements, strings, and/or nulls;

  • The name is render and the value is a JSON object with arguments for the rendering process.

All property names are optional and their values default to unset if not given, with one exception: the property name tag defaults to div if not given. In other words, if not present, the name/value pair "tag": "div" is assumed to be implicitly given.

The tag property defines the type of HTML element to render while the data property defines the contents of the element and the compositional relationship between elements.

Example: Two nested elements
{
    "tag": "h1",
    "data": "Hello, World!",
    "tag": "a",
    "href": "https://proximify.com",
    "data": {
        "tag": "img",
        "src": "hello_world.png"
    }
}
<!-- Rendered HTML Output (body contents only) -->
<h1>Hello, World!</h1>
<a href="https://proximify.com">
    <img src="hello_world.png" />
</a>

The given definition of GLOT ensures that it is a strict superset of HTML, which in turn means that the language can define any valid HTML page.

It's important to note that while an element is a JSON object, not all JSON objects are elements.

Widget Elements

GLOT elements can control the addition of HTML, JS, CSS and assets into an output webpage. The explanation of such mechanics requires the definition of some terms.

Definitions

An HTML element is an element with an unset or empty widget property.

A widget element is an element with a non-empty widget property. A widget element defines the instantiation parameters of a widget package within a webpage. The term widget alone is considered to be an alias for widget element.

A widget instance is the result of rendering a widget into a webpage. It is composed of the HTML, JS, CSS and assets generated by the rendering process of the widget.

A widget container is the rendered HTML element that corresponds to a widget element, and whose contents are to be rendered recursively.

A widget definition is the abstract concept of explaining how a widget element is to be instantiated into a webpage as a function of its instantiation parameters.

A widget package is a widget definition given as a set of server-side PHP files, client-side JavaScript files, CSS, and assets. A widget package must define a rendering rule, which is a function that generates HTML, JS, CSS code for a webpage.

Mechanics

The objective of a widget package is to capture web patterns that can be parameterized to achieve a meaningful range of useful renderings.

The standard method to define the rendering rule of a widget is to define a class with a render() method that takes data and params arguments and returns a value that can be processed recursively.

The GLOT language specification does not require a particular implementation language for a widget class. In fact, the objective is to have some freedom in the choice of languages so that different web communities can benefit from widgets coded in different programming languages, such as PHP and NodeJS.

Since a widget is a JSON object that represents a specific instantiation of a widget package in a webpage, the objective of the render method is to transform the JSON object into a widget container and to request any needed JavaScript and CSS for the output webpage.

The render(data, params) method receives as arguments the data property of the widget, and a localized version of its params property. The method does not receive the widget itself, i.e., the JSON object with its other possible properties, such as its tag. However, the method can modify such properties via its return value.

The return value of the render method must be

  • an array representing an an element (i.e. an array with at least one non-integer key);
  • a string;
  • null; or
  • a numerically-indexed array with items of any of the three types above.

The meaning of the return value might be either:

  • a widget container with its contents; or
  • only the contents of the widget container whose tag is defined by the JSON object, or DIV if not given.

The two cases are disambiguated by interpreting the return value as follows:

  • If it is an array with a tag key and no widget key, then it is taken as the entire widget container;
  • Otherwise, it is taken as only the contents of the widget container.

Regardless of which case the return value falls into, the contents of a widget are processed recursively in order to complete the instantiation the widget.

Rendering

The rendering process replaces all widget elements with new HTML elements according to rendering rules and the render methods of each widget class.

The transformation of a widget element into an HTML element preserves all properties (HTML attributes) of the original element except for the properties: widget, data, params and render. The result of the transformation is an element with HTML attributes and contents that can be rewritten, as output, using HTML syntax.

When the return value of the render method is a widget container, the value is shallow-merged into the resulting HTML element. The merge operation keeps the attributes of the element that do not exist in the return value, and replaces the attributes that exists in both with those of the return value. One exception is the tag key, which is given special consideration when merging. If the tag key in the return value is empty (e.g., null, false, ""), then the tag value of the original element is preserved.

Example: A widget's rendering rule
/**
 * Server-side rendering of the SearchBar widget.
 */
class SearchBar extends Widget
{
    /**
     * Define the generative rule of the search bar.
     */
    public function render($data, $params)
    {
        if ($params['mode'] == 'compact')
            $markup = ['tag' => 'section'];
        } else {
            $markup = ['tag' => 'span'];
        }

        // Passthrough data for recursive evaluation
        $markup['data'] = $data;

        return $markup;
    }
}

The render() method is invoked by the inclusion of a widget in a GLOT page, or by an array of elements returned by another render method. For example,

{
    "widget": "SearchBar",
    "data": { "tag": "span", "data": [...] },
    "params": { "mode": "compact" }
}

requests the rendering of a SearchBar widget based on the given data and params values.

The example above does not set an explicit tag for the widget, so the tag is assumed to be div. However, in this example, the render() method returns a non-numeric array with a tag and data. The return value is interpreted as a widget container (i.e., not just contents). The final widget container overrides the assumed div tag with the returned one.

Localizable text and values

The second pillar of GLOT is its ability to naturally represent multiple language versions of an HTML webpage. Such an ability is powered by the concepts of localizable values.

It is recommended to include a list of valid languages for a website in settings/languages.json as an ordered array of language codes. For example

// Website languages defined in settings/languages.json
["en", "fr", "es-ar", "zh"]

defines 4 languages: English (en), French (fr), Spanish-Argentina (es-ar) and Chinese (zh).

The first language in the array is considered to be the default source language for all translations that don't define an explicit source. Requests for undefined language values are assigned fallback values at rendering time according to missing language rules that depend on the array of languages.

The GLOT languages allows for language-dependent values stored inline within a page or externally in dictionary files. It also allows for hybrid scenarios in which some language-dependent values are inline while others are external. Each language-dependent value can define which language is the source language for it.

Localizable values

A localizable value is a JSON object with name/value pairs such that:

The $ name is a metadata key whose value is metadata of the localizable value, such as a dictionary key or editing status.

A dictionary object is a JSON object that collects information about a dictionary entry, such as: value, editing status, and localization settings. Its properties must conform to the dictionary object schema.

A dictionary file is a JSON file with one root object mapping dictionary keys to dictionary objects. There can be at most one dictionary file per page and language. The path to the dictionary file must be dictionaries/{relative-page-path}/{page-name}/{language-tag}.json.

A localizable text is defined as a localizable value with the only difference that all of its language tags map to a string or null value. Localizable text objects can be used as values for HTML attributes that expect a string type.

Important considerations

  1. All language and region tags must be lowercase.
  2. Not all declared language tags need to be defined in each localizable text.
  3. It is not an error to provided language values for undeclared languages.
  4. A null language value is equivalent to an undefined language value.
  5. The inline value for a language has presence over its dictionary value.
  6. Only one language in a localizable value can flagged as the source language.
  7. Undefined language values are assigned fallback values at rendering time according to missing language rules.

For example, the following localizable text

{
    "$": 201, // Dictionary keys for all external languages
    "$ch": "alt-key", // Optional per-language key?
    "en": "Hello World!",
    "es-ar": "¡Hola Mundo!",
    "$en": {
        "isSource": true,
        "status": "reviewing"
    },
    "$es-ar": {
        "status": "pending",
        "author": "Diego"
    }
}

defines 3 language tags, with English as the source language. The translations for Spanish-Argentina is given inline, while the Chinese translation is given in a dictionary file under the dictionary key 201. In this object, the translation for French is undefined.

The alternative compact syntax:

{
    "$": 201,
    "$en": {
        "value": "Hello World!",
        "isSource": true,
        "status": "reviewing"
    },
    "$es-ar": {
        "value": "¡Hola Mundo!",
        "status": "pending",
        "author": "Diego"
    }
}

can be used to define a values in a reference object. If a value exist both in a reference object an in a regular language property, the one in the regular property has precedence. For example, in a case like

{
    "en": "higher precedence",
    "$en": {
        "value": "lower precedence"
    }
}

the final value for English would be "higher precedence". In other words, the logic is as follows:

$text['en'] ??= $text['$en']['value'] ?? (($key = $text['$'] ?? false) ? getDictionaryData($key) : null);

A single dictionary key can be given for all languages. For example,

{
    "tag": "a",
    "href": { "$": 201 }
}

instructs the Renderer to lookup the dictionary key 201 in the dictionary of any requested language in order to localize the href attribute of the anchor tag.

Localizable text

The localizable text objects can be used as values for element properties and for widget parameters. For example,

{
    "widget": "Picture",
    "src": {
        "en": "forest_in_england.png",
        "fr": "forest_in_france.png"
    },
    "params": {
        "mode": "compact",
        "caption": {
            "en": "A nice forest",
            "fr": "Une belle forêt"
        }
    }
}

defines localizable text for the src property of the widget, and for the caption parameter of the widget.

Pages

A file with GLOT markup is considered to be a single webpage. The definition of what constitutes valid contents for a page and the roles that different elements play in the rendering process requires the introduction of a few concepts.

A page given as a JSON file is a valid GLOT page if its contents are one of the following:

  • an element;
  • a single string;
  • a null value; or
  • an array of: element, string, and/or null values.

A page cannot have other types of root-level entities, such as a JSON object that is not an element, a boolean, an integer, or an array of non-valid root-level entities.

A page widget is the first element of a page whose content is a root element that is a widget.

For example, in the following file contents,

{
    "widget": "Page",
    "data": [
        { "widget": "Navbar" },
        { "widget": "Section" },
        { "widget": "Section" },
        { "widget": "Footer" }
    ]
}

the root widget, Page, is considered to be the page widget.

Master pages

Websites often have pages with similar structures, such as a common navbar and footer. In GLOT, such patterns can be expressed using master pages and super-master pages.

A master page is a page that is referenced from another page as its template. Only a page container can reference a master page. The rendering process for a page referencing a master page is to first render the master page and then merge the rendering of the page into it.

Example: A page referencing a master page
{
    "widget": "Page",
    "render": {
        "template": "_masters/base_page"
    },
    "data": [
        { "widget": "Navbar" },
        { "widget": "Section" },
        { "widget": "Section" },
        { "widget": "Footer" }
    ]
}

By merging the renderings of a page into that of a master page, one can represent both the commonality across pages and their individual variability of each page. In some advanced cases, capturing patterns across pages also requires capturing commonalities across master pages.

A super-master page is a master page that is referenced by another master page. A super-master page is rendered first, then the rendering of the referrer master page is merged into it, and finally the rendering of the referrer page is merged into the result.

The super-master pages represents second-degree commonalities across website pages. A page can be both a master page for some pages and a super-master page for others. Master and super-master are just roles that determine the merging order of three GLOT pages in order to generate a single output HTML page.

Since a super-master page is a regular master page, it can be used as such by a regular page. The only special property of a super-master page is that it cannot reference another master page.


Next steps

  1. Learn about the GLOT framework

  2. Follow a visual tutorial on website building with GLOT

  3. Learn how to create widget packages

  4. Search the Glot Bazaar for solutions to your website needs

Contributing

This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to and actually do, grant us the rights to use your contribution. For details, visit our Contributor License Agreement (CLA).

When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.

This project has adopted the Proximify Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact support@proximify.com with any additional questions or comments.

License

Copyright (c) Proximify Inc. All rights reserved.

Licensed under GPLv2 (or later) from the Free Software Foundation.

GLOT is made by Proximify. We invite the community to participate.