Skip to content

Dashboard Customization

Lunaria provides several ways to customize your localization dashboard to better fit your project’s look and feel. Follow this guide to understand all the available customization options.

Title and description

By default, Lunaria will add a default title and description for your dashboard. You can change it to make it easy to identify and find your project’s dashboard through search engines.

  1. Add the dashboard.title option to your Lunaria config. The defined title will also be shown in the dashboard’s heading:

    lunaria.config.json
    {
    "dashboard": {
    "title": "Lunaria Localization Status"
    }
    }
  2. Add the dashboard.description option to your Lunaria config:

    lunaria.config.json
    {
    "dashboard": {
    "title": "Lunaria Localization Status",
    "description": "Localization status dashboard for the Lunaria project."
    }
    }

Canonical URL

If you desire to publicize different versions of your dashboard (e.g. localized versions of the dashboard itself), it might be helpful to set a canonical URL to optimize your dashboard’s ranking on search engines.

To add a canonical URL, you can set dashboard.site in your Lunaria configuration file:

lunaria.config.json
{
"dashboard": {
"site": "https://localization.lunaria.dev/"
}
}

Favicons

Setting a favicon can help match your project’s feel and make it easier to identify your dashboard in the user’s web browser. Lunaria allows you to define both multiple external and one inline favicon.

To add a favicon, you have to set dashboard.favicon in your Lunaria configuration, with one of, or both the external or inline properties.

External favicons

External favicons are favicons you load through another URL as a resource. You can add them in the external property of dashboard.favicon. The example below adds both .svg and .ico favicons.

lunaria.config.json
{
"dashboard": {
"favicon": {
"external": [
{
"link": "https://lunaria.dev/favicon.svg",
"type": "image/svg+xml"
},
{
"link": "https://lunaria.dev/favicon.ico",
"type": "image/x-icon"
},
]
}
}
}

Inline favicon

An inline favicon is a local .svg favicon appended to your dashboard through a Data URI. You can add one through the inline property of dashboard.favicon.

  1. Add your favicon SVG file to a directory in your project (e.g. src/assets/):

    • Directorysrc/
      • Directoryassets/
        • favicon.svg
    • lunaria.config.json
  2. Add the path to your favicon as the inline property of dashboard.favicon in your Lunaria config:

    lunaria.config.json
    {
    "dashboard": {
    "favicon": {
    "inline": "./src/assets/favicon.svg"
    }
    }
    }

Hidden path bases

The dashboard contains several paths as links to your source and localized content. These can be quite lengthy depending on how far they are from your project’s root directory. For example, if the unique part of your content files’ paths starts only after src/content/docs/, you might want to hide this portion of the path and only show what’s relevant, e.g. guides/my-content.mdx.

To do so, you can set the dashboard.basesToHide option in your Lunaria config with all the desired path bases to hide. This example assumes you’re tracking content files that start at src/content/docs/ or src/i18n/:

lunaria.config.json
{
"dashboard": {
"basesToHide": ["src/content/docs/", "src/i18n/"]
}
}

With this, references to paths like src/content/docs/guide.mdx and src/i18n/dictionary.ts will be shortened to their unique parts, namely guide.mdx and dictionary.ts.

Custom CSS

The dashboard can be further customized by adding your own custom CSS, allowing you to modify or extend the default styles provided by Lunaria out of the box.

  1. Add a CSS file to a directory in your project (e.g. src/styles/). For example, you can change the default body font family used:

    src/styles/lunaria.css
    :root {
    --ln-font-body: Tahoma, sans-serif;
    }
  2. Add the path to your CSS file to your Lunaria config’s dashboard.customCss array property:

    lunaria.config.json
    {
    "dashboard": {
    "customCss": ["./src/styles/lunaria.css"]
    }
    }

Custom labels and language

By default, Lunaria’s dashboard comes with UI labels written in English. These UI labels can be completely changed, be it simple text labels, language values, or dynamic sentences (e.g. “X done, X outdated, X missing”).

Text labels

For example, you can change the default message shown when a locale’s localization is 100% complete by changing the statusByLocale.completeLocalization property of dashboard.ui:

lunaria.config.json
{
"dashboard": {
"ui": {
"statusByLocale.completeLocalization": "No missing or outdated changes found, congratulations!"
}
}
}

Format labels

Some labels, suffixed with Format in their name, come with named {} blocks that are going to be dynamically replaced with values from other UI labels or from the internal context, for example:

"statusByLocale.detailsTitleFormat": "{locale_name} ({locale_tag})",

These can also be changed, though it is highly recommended to not remove any of the {} blocks present by default, as that would mean possibly not inserting a dynamic value that was expected.

For example, you could modify your dashboard to show the language details’ summary from the format Deutsch (de) to Deutsch - de by making a small change in the default statusByLocale.detailsTitleFormat value:

lunaria.config.json
{
"dashboard": {
"ui": {
"statusByLocale.detailsTitleFormat": "{locale_name} - {locale_tag}",
}
}
}

lang and dir

Since all labels can be changed, there is no imposition from completely translating the dashboard to another language. To make this even easier, dashboard.ui exposes the lang and dir properties, which directly translates to the HTML lang and dir attributes used in your dashboard.

For example, that’s how you could go about changing your dashboard’s language to Brazilian Portuguese (most properties have been omitted from this example for simplicity’s sake):

lunaria.config.json
{
"dashboard": {
"ui": {
"lang": "pt-BR",
"statusByFile.heading": "Estado da localização por arquivo",
"statusByFile.tableRowFile": "Arquivo",
}
}
}

Custom UI elements

Lunaria’s dashboard was designed to be flexible and easily customizable through custom CSS and the available configuration options. When that’s not enough, Lunaria allows you to extend and override your dashboard’s UI using its Renderer API.

  1. Add a new renderer.config.ts or renderer.config.js file to your project with the following content:

    renderer.config.ts
    import { defineRendererConfig } from '@lunariajs/core';
    export default defineRendererConfig({
    // options will go here...
    });
  2. Add the path to your renderer file to your Lunaria config’s renderer configuration property:

    lunaria.config.json
    {
    "renderer": "./renderer.config.ts"
    }

Custom components

All UI elements in Lunaria are built without the use of frameworks, using only the good ol’ HTML. To improve the authoring experience, Lunaria comes with a built-in html tagged template to help you build your own components.

A component in Lunaria is a function that receives a few parameters and returns a string of HTML:

example-component.ts
import { html } from '@lunariajs/core';
const CustomParagraph = () => html`<p>This is my custom paragraph component!</p>`;

Since these components are template literals, you can interpolate values using the ${} notation as you normally would in JavaScript:

interpolating-value.ts
import { html } from '@lunariajs/core';
const favoriteColor = "red";
const ColorComponent = () => html`<p>My favorite color is ${favoriteColor}!</p>`.
See the resulting HTML
<p>My favorite color is red!</p>

You can also interpolate over lists of values (e.g. arrays) using the .map() method:

interpolating-list.ts
import { html } from '@lunariajs/core';
const catColors = ["grey", "white", "black", "orange", "buff"];
const CatColorsList = () => html`<ul>${catColors.map((catColor) => html`<li>${catColor}</li>`)}</ul>`
See the resulting HTML
<ul>
<li>grey</li>
<li>white</li>
<li>black</li>
<li>orange</li>
<li>buff</li>
</ul>

Slotting elements

Elements can be slotted into the dashboard with the renderer’s slots property. All components will be passed a copy of your Lunaria configuration you can use to get values from:

  1. Create a new component using the html tagged template. In this case, a "robots" meta tag with the "noindex" content to avoid indexing the dashboard in search engines:

    renderer.config.ts
    import { defineRendererConfig, html } from '@lunariajs/core';
    const NoIndex = () => html`<meta name="robots" content="noindex" />`;
    export default defineRendererConfig({});
  2. Add the created component to one of the available slots in the slots property. Here, the head slot will be used to insert the component into the dashboard’s <head> element:

    renderer.config.ts
    import { defineRendererConfig, html } from '@lunariajs/core';
    const NoIndex = () => html`<meta name="robots" content="noindex" />`;
    export default defineRendererConfig({
    slots: {
    head: NoIndex,
    }
    });

Overriding elements

Elements can also be overridden in the dashboard with the renderer’s overrides property. All components added to overrides will be passed a copy of your Lunaria configuration, as well as the generated Lunaria status (except for the meta property) you can use to get values from:

  1. Create a new component using the html tagged template. In this case, the component will override the dashboard’s body and show the sharedPath for each localized content:

    renderer.config.ts
    import { defineRendererConfig, html } from '@lunariajs/core';
    const NewBody = (config, status) => html`<ul>${status.map((s) => html`<li>${s.sharedPath}</li>`)}</ul>`;
    export default defineRendererConfig({});
  2. Add the created component to one of the available overrides in the overrides property. Here, the body slot will be used to insert the component and override everything inside the dashboard’s <body> element:

    renderer.config.ts
    import { defineRendererConfig, html } from '@lunariajs/core';
    const NewBody = (config, status) => html`<ul>${status.map((s) => html`<li>${s.sharedPath}</li>`)}</ul>`;
    export default defineRendererConfig({
    overrides: {
    body: NewBody,
    }
    });