Alternate Take on Zine's Site Schema

  •   〜 4 min read   •   by: aurumcodex

An effort to merge both `Site` and `Multilingual` Ziggy types

Allow me to preface with this: I like Zine. I like it quite a bit. Extending standard HTML with a few extra tags and some custom attributes is genius in a simple manner. But, Zine is still in alpha, and I’ve not been able to find a proper specification for either the Site nor the Multilingual schema for use in the zine.ziggy file.

Now, I also realize that Zine uses Ziggy, also made by the same person (@kristoff-it), and it too is also in a very early alpha state. But, it does have some neat features that I think the zine.ziggy file could take advantage of. This article seeks to try and “standardize” (I’m using that term very loosely) declaring a Zine Site.

The “new” specification

The new specification idea is to combine both the single-language Site with the multi-language Multilingual into a tagged union available via the new .ziggy-schema. This allows having both content types in one unified specification, as well as some extras thrown in, like the ability to use external layouts and stylesheets (and other assets) from other modules that could be imported via zig fetch.

Why would you want this? Well, I’d start with using Zine as a documentation site generator, akin to how some projects use Sphinx or mdBook to generate their documentation. Using external layouts and stylesheets and other assets (which I’ll refer to as a “theme” for simplicity) would allow users of Zine to quickly get up and running with writing their documentation, rather than trying to write specific style rules or try and figure out which JavaScript libraries/frameworks they would need; it would all be done, and available. This sort of already can be done, but it’s not what I would call a first-class solution; it can be done, though.

Anyhow, here’s the new proposed specification with doc comments:

$ = Zine

/// A representation of Zine site to be built.
struct Zine {
  /// The target URL that the site is supposed to point to.
  host_url: bytes,

  /// A struct that handles the content of the site.
  /// Takes a string that denotes the `base_dir` of the content.
  content: Content,

  /// A struct that handles the layouts of the site.
  layouts: Layouts,

  /// A struct that handles the various assets of the site.
  assets: Assets,

  /// The `Content` struct deals with the raw content of the Zine site.
  /// It can be either in a single language, or multilingual.
  struct Content {
    /// The base directory to search for content files.
    base_dir: bytes,

    /// The type of content that the site will make available:
    /// single language or multilingual.
    type: ContentType,

    /// The is the tagged union type that represents which type of content
    /// or content layout gets used by the Zine site.
    union ContentType {
      /// Uses a single language version of the Zine site.
      single: SingleLang,

      /// Uses a multilingual version of the Zine site.
      multilingual: Multilingual,
    }
  }

  /// A struct representing the SingleLang version of a Zine site.
  struct SingleLang {
    /// The language code that the site still uses.
    /// Useful to set, but if not needed, it can be set to null.
    /// (Allows setting)
    lang: ?bytes,

    /// The (renderable) title of the Zine site.
    title: bytes,
  }

  /// A struct representing the Multilingual version of a Zine site.
  struct Multilingual {
    /// Directory to search for localization files.
    ///
    /// (Theoretically, this could be relative to the project directory 
    /// (where `zine.ziggy` resides),  or it could be relative to the content
    /// directory (i.e.: `content/i18n/<locale code>/`)).
    i18n_dir: bytes,

    /// An array of `Locale`s, or a dictionary of `Locale`s, which represent
    /// all of the localizations and translations that are provided.
    locales: []Locale, // or {:}Locale
  }

  /// A struct representing potential Layouts for the site.
  struct Layouts {
    /// The (local) base directory to look for layout files.
    base_dir: bytes,

    /// A dictionary of external directories to look into for layout files.
    /// If set to null, it disables checking for any kind of external
    /// layouts to be used.
    ///
    /// In order to use files from the external dir(s), access via:
    /// "<key>:<file path>"
    external_dirs: ?{:}bytes,
  }

  /// A struct representing potential Assets for the site.
  struct Assets {
    /// The (local) base directory to look for asset files.
    base_dir: bytes,

    /// A dictionary of external directories to look into for asset files.
    /// If set to null, it disables checking for any kind of external
    /// layouts to be used.
    ///
    /// In order to use files from the external dir(s), access via:
    /// "<key>:<file path>"
    external_dirs: ?{:}bytes,

    /// A(n) list / array of asset paths
    static_assets = ?[]bytes,
  }

  /// A struct defining a "Locale" to be used in a Multilingual site.
  struct Locale {
    /// The language code that the Locale uses.
    /// (Would be unused if using the dictionary method.)
    code: bytes,

    /// The name of the language used by the Locale.
    name: bytes,

    /// The Zine site's title, written in the language represented by the Locale.
    title: bytes,

    /// The sub-directory to use for the particular language to use for the site.
    /// If set to null, then it defaults to a sub-directory with the same name as
    /// the `.code` field or the key used for the Locale entry.
    content_subdir: ?bytes,

    /// An optional string that redirects the URL paths to different URL subdomains.
    host_url_override: ?bytes,

    /// An optional string that renames the site's build paths
    /// for multilingual sites.
    output_prefix_override: ?bytes,
  }
}
The “improved” ziggy-schema

Quite featureful, no? Has just about everything (at least, I think so) that you’d possibly need for either a single-language site, or one that is planning to be multilingual. You may notice that there are a few spots with a comment like: // or {:}Locale. This is because I’m not 100% sure on which version would be a better representation of the site. (I’m biased, but I like the dictionary method more.)

If you aren’t convinced yet, well…

New spec in usage

I took the current zine.ziggy file that https://ziglang.org uses (which, it’s pretty neat that Zig’s own website uses Zine, but I digress) and translated it to the “new” specification.

Using the []Locale style declarations:

/// This describes a Multilingual Zine Site:
/// (using the `ziglang.org` `zine.ziggy` as reference)
Zine {
  .host_url = "https://example.com",
  .content = {
    .base_dir = "content",
    .type = .multilingual({
      .i18n_dir = "i18n",
      .locales = [
        {
          .code = "en-US",
          .name = "English (original)",
          .title = "Zig Programming Language",
          .content_subdir = null, // if null (or not supplied): in a multilingual
                                  // context, default matches the string in `.code`
          .output_prefix_override = "",
        },
        {
          .code = "es-AR",
          .name = "Español",
          .title = "El Lenguaje de Programación Zig",
        },
        {
          .code = "ru-RU",
          .name = "Русский",
          .title = "Язык программирования Zig",
        },
        {
          .code = "it-IT",
          .name = "Italiano",
          .title = "Zig Programming Language",
        },
        {
          .code = "de-DE",
          .name = "Deutsch",
          .title = "Zig Programmiersprache",
        },
        {
          .code = "uk-UA",
          .name = "Українська",
          .title = "Zig Programming Language",
        },
        {
          .code = "ja-JP",
          .name = "日本語",
          .title = "Zig Programming Language",
        },
        {
          .code = "zh-CN",
          .name = "中文",
          .title = "Zig 编程语言",
        },
        {
          .code = "ko-KR",
          .name = "한국어",
          .title = "Zig 프로그래밍 언어",
        },
      ]
    }),
  },
  .layouts = {
    .base_dir = "layouts",
    .external_dirs = null,
  },
  .assets = {
    .base_dir = "assets",
    .external_dirs = null,
    .static_assets = [
      ".well-known/funding-manifest-urls",
      "funding.json",
      "external-link-dark.svg",
      "external-link-light.svg",
      "heart.svg",
      "zig-logo-dark.svg",
      "zig-logo-light.svg",
      "zig-performance-logo-dark.svg",
      "zig-performance-logo-light.svg",
      "sponsors/Blacksmith_Logo-Black.svg",
      "sponsors/Blacksmith_Logo-White.svg",
      "sponsors/coil-logo-black.svg",
      "sponsors/coil-logo-white.svg",
      "sponsors/dropbox.png",
      "sponsors/lavatech.png",
      "sponsors/pex-dark.svg",
      "sponsors/pex-white.svg",
      "sponsors/scaleway.png",
      "sponsors/shiguredo-logo-dark.svg",
      "sponsors/shiguredo-logo-light.svg",
      "sponsors/tb-logo-black.png",
      "sponsors/tb-logo-white.png",
      "sponsors/zml.svg",
      "sponsors/SILARES_HORIZONTAL_LOGO_WHITE.png",
      "sponsors/SILARES_HORIZONTAL_LOGO_BLACK.png",
      "chart/chartist-1.3.0.css",
      "chart/chartist-1.3.0.umd.js",
      "download/0.15.1/release-notes/build-webui.png",
      "download/0.15.1/release-notes/build-webui-time-report.png",
    ],
  },
}
Usage of the “improved” zine.ziggy

And using the {:}Locale style declarations:

/// This is an alternate take on a Multilingual Zine Site
/// (using the `ziglang.org` `zine.ziggy` as reference)
/// (Using a dictionary instead of array of structs)

Zine {
  .host_url = "https://ziglang.org",
  .content = {
    .base_dir = "content",
    .type = .multilingual({
      .i18n_dir = "i18n",
      .locales = {
        "en-US": {
          .name = "English (original)",
          .title = "Zig Programming Language",
          .content_subdir = null, // if set to null (or not given),
                                  // check for a path equal to the key,
                                  // otherwise, error out
          .output_prefix_override = "", // set to empty string for default
        },
        "es-AR": { .name = "Español", .title = "El Lenguaje de Programación Zig" },
        "ru-RU": { .name = "Русский", .title = "Язык программирования Zig" },
        "it-IT": { .name = "Italiano", .title = "Zig Programming Language" },
        "de-DE": { .name = "Deutsch", .title = "Zig Programmiersprache" },
        "uk-UA": { .name = "Українська", .title = "Zig Programming Language" },
        "jp-JP": { .name = "日本語", .title = "Zig Programming Language" },
        "zh-CN": { .name = "中文", .title = "Zig 编程语言" },
        "ko-KR": { .name = "한국어", .title = "Zig 프로그래밍 언어" },
      },
    }),
  },
  .layouts = {
    .base_dir = "layouts",
    .external_dirs = null,
  },
  .assets = {
    .base_dir = "assets",
    .external_dirs = null,
    .static_assets = [
      ".well-known/funding-manifest-urls",
      "funding.json",
      "external-link-dark.svg",
      "external-link-light.svg",
      "heart.svg",
      "zig-logo-dark.svg",
      "zig-logo-light.svg",
      "zig-performance-logo-dark.svg",
      "zig-performance-logo-light.svg",
      "sponsors/Blacksmith_Logo-Black.svg",
      "sponsors/Blacksmith_Logo-White.svg",
      "sponsors/coil-logo-black.svg",
      "sponsors/coil-logo-white.svg",
      "sponsors/dropbox.png",
      "sponsors/lavatech.png",
      "sponsors/pex-dark.svg",
      "sponsors/pex-white.svg",
      "sponsors/scaleway.png",
      "sponsors/shiguredo-logo-dark.svg",
      "sponsors/shiguredo-logo-light.svg",
      "sponsors/tb-logo-black.png",
      "sponsors/tb-logo-white.png",
      "sponsors/zml.svg",
      "sponsors/SILARES_HORIZONTAL_LOGO_WHITE.png",
      "sponsors/SILARES_HORIZONTAL_LOGO_BLACK.png",
      "chart/chartist-1.3.0.css",
      "chart/chartist-1.3.0.umd.js",
      "download/0.15.1/release-notes/build-webui.png",
      "download/0.15.1/release-notes/build-webui-time-report.png",
    ],
  },
}
Usage of the “improved” zine.ziggy (with Locale dictionary)

Assuming that you have many languages needed for your website, accessing the data via SuperHTML (Zine’s templating language) could be as easy as $page.i18n("<locale>").code for the array based implementation, or via $page.i18n("<locale>").key for the dictionary based implementation.

What’s that? You’re saying that I neglected to showcase the single-language version?

For single language sites

You just simply follow the same principles, but with content.type set to .single() instead.

For example:

/// This describes a Single-Language Zine Site:
Zine {
  .host_url = "https://example.com",
  .content = {
    .base_dir = "content",
    .type = .single({
      .lang = "en-US",
      .title = "Example Zine Site",
    }),
  },
  .layouts = {
    .base_dir = "layouts",
    .external_dirs = {
      "external": "zig-pkg/custom_theme/layouts",
    },
  },
  .assets = {
    .base_dir = "assets",
    .external_dirs = {
      "external": "zig-pkg/custom_theme/assets",
    },
    .static_assets = [
      "favicon.ico",
      "mathtex/Temml.woff2",
      "fonts/STIXTwoMath/STIXTwoMath-Regular.otf",
      "external:scripts/custom_script.js", // for example.
    ],
  },
}
Usage of the “improved” zine.ziggy (for single language site)

(The above example even showcases using external theme assets!)

To wrap up

I hope I’ve showcased how Zine could be improved with Ziggy in the future.

Hell, for all I know, it’ll remain the same way it is now permanently. I kind of doubt it, but even if it does, Zine’s still a wonderful piece of software, and time will only make it better.

(Curious about Zine? Check the footer of the site; there’s a link to it there.)