Add bilingual homepage and self-hosted fonts
This commit is contained in:
@@ -0,0 +1,211 @@
|
||||
---
|
||||
import ManualLayout from '../layouts/ManualLayout.astro';
|
||||
import { highlightCode } from '../lib/highlight.js';
|
||||
|
||||
const { locale = 'en' } = Astro.props;
|
||||
|
||||
const translations = {
|
||||
en: {
|
||||
title: 'Decodal — Deferred Constraint Data Language',
|
||||
description: 'Decodal is a deterministic data language for describing, composing, validating, and materializing structured data.',
|
||||
heroTitle: 'Configuration, constraints, and defaults are values.',
|
||||
heroLead: 'Decodal is a small deterministic language for describing, composing, validating, and materializing structured data. The same expression can carry concrete data and the range that data must satisfy.',
|
||||
playground: 'Open the playground',
|
||||
introduction: 'Read the introduction',
|
||||
facts: [
|
||||
['Source', '.dcdl'],
|
||||
['Runtime', 'Rust · WebAssembly'],
|
||||
['Output', 'Validated structured data'],
|
||||
],
|
||||
exampleLabel: 'Decodal example and materialized result',
|
||||
materialize: 'materialize',
|
||||
modelTitle: 'One expression system, from abstract range to concrete data.',
|
||||
definitions: [
|
||||
['&', 'Compose without discarding constraints', 'Symmetric composition keeps information from both sides and fails when their ranges cannot overlap.'],
|
||||
['as', 'Verify that a value is more specific', 'The left side must fit inside the wider range on the right. Wider abstract fields remain available for later materialization.'],
|
||||
['//', 'Apply an explicit structural patch', 'Right-biased composition replaces the selected structure while preserving unrelated fields.'],
|
||||
['default', 'Choose a fallback only when materializing', 'A default does not erase the range. It is selected only when no more concrete value has been supplied.'],
|
||||
],
|
||||
hostTitle: 'The host owns I/O. Decodal owns evaluation.',
|
||||
hostDescription: 'Filesystems, Markdown, JSON, network resources, and application globals stay under host control. Decodal receives source or structured values and evaluates them with the same rules in production and editor tooling.',
|
||||
flowLabel: 'Evaluation flow',
|
||||
flow: [
|
||||
['Host environment', 'globals · source · structured imports'],
|
||||
['Evaluate', 'lazy references · constraints · composition'],
|
||||
['Materialize', 'defaults · validation · diagnostics'],
|
||||
['Application data', 'Data · Rust type · JavaScript value'],
|
||||
],
|
||||
embedding: 'Read the embedding guide',
|
||||
packagesTitle: 'Use only the layer your application needs.',
|
||||
packages: [
|
||||
['Runtime, host embedding, schema derivation, and typed decoding.'],
|
||||
['Browser runtime and host-configurable semantic language service.'],
|
||||
['Language Server Protocol with application-defined environments.'],
|
||||
['CodeMirror 6 syntax, indentation, folding, highlighting, and formatter integration.'],
|
||||
],
|
||||
packagesLink: 'Compare packages and integrations',
|
||||
},
|
||||
ja: {
|
||||
title: 'Decodal — 制約とデフォルトを値として扱うデータ言語',
|
||||
description: 'Decodalは、構造化データの記述、合成、検証、具体化のための決定的なデータ言語です。',
|
||||
heroTitle: '設定、|制約、|デフォルトは、|すべて値です。',
|
||||
heroLead: 'Decodalは、|構造化データを|記述・合成・検証・具体化するための、|小さく決定的な言語です。|同じ式の中に、|具体的なデータと、|そのデータが満たすべき範囲を|保持できます。',
|
||||
playground: 'Playgroundを開く',
|
||||
introduction: 'はじめに読む',
|
||||
facts: [
|
||||
['ソース', '.dcdl'],
|
||||
['ランタイム', 'Rust · WebAssembly'],
|
||||
['出力', '検証済みの構造化データ'],
|
||||
],
|
||||
exampleLabel: 'Decodalのコード例と具体化結果',
|
||||
materialize: '具体化',
|
||||
modelTitle: '抽象的な範囲から|具体的なデータまで、|ひとつの式体系で。',
|
||||
definitions: [
|
||||
['&', '制約を失わずに|合成する', '対称な合成は|両辺の情報を残し、|範囲が重ならない場合は|失敗します。'],
|
||||
['as', '値がより具体的であることを|検証する', '左辺は、|右辺のより広い範囲に|収まる必要があります。|右辺に残る抽象的なフィールドは、|後の具体化に|利用できます。'],
|
||||
['//', '構造を明示的に|上書きする', '右辺優先の合成で、|指定した構造を置き換えながら、|関係のないフィールドを|保持します。'],
|
||||
['default', '具体化するときだけ|フォールバックを選ぶ', 'defaultは範囲を消しません。|より具体的な値が|与えられていない場合にだけ|選択されます。'],
|
||||
],
|
||||
hostTitle: 'I/Oはホストが担い、|評価はDecodalが担う。',
|
||||
hostDescription: 'ファイルシステム、|Markdown、JSON、ネットワーク資源、|アプリケーションのグローバル値は、|ホストの管理下に置かれます。|Decodalはソースまたは|構造化された値を受け取り、|本番環境とエディタツールで|同じ規則を使って評価します。',
|
||||
flowLabel: '評価の流れ',
|
||||
flow: [
|
||||
['ホスト環境', 'globals · source ·|構造化import'],
|
||||
['評価', '遅延参照 · 制約 ·|合成'],
|
||||
['具体化', 'default · 検証 ·|診断'],
|
||||
['アプリケーション|データ', 'Data · Rust型 ·|JavaScript値'],
|
||||
],
|
||||
embedding: '組み込みガイドを読む',
|
||||
packagesTitle: 'アプリケーションに|必要な層だけを|使えます。',
|
||||
packages: [
|
||||
['ランタイム、|ホストへの組み込み、|スキーマ導出、|型付きデコード。'],
|
||||
['ブラウザランタイムと、|ホストから設定できる|セマンティック言語サービス。'],
|
||||
['アプリケーション定義の環境を|利用できる|Language Server Protocol実装。'],
|
||||
['CodeMirror 6向けの構文、|インデント、折りたたみ、|ハイライト、|フォーマッタ連携。'],
|
||||
],
|
||||
packagesLink: 'パッケージと統合方法を比較する',
|
||||
},
|
||||
};
|
||||
|
||||
const copy = translations[locale] ?? translations.en;
|
||||
const escapeHtml = (text) => String(text)
|
||||
.replaceAll('&', '&')
|
||||
.replaceAll('<', '<')
|
||||
.replaceAll('>', '>')
|
||||
.replaceAll('"', '"')
|
||||
.replaceAll("'", ''');
|
||||
const phrase = (text) => locale === 'ja'
|
||||
? String(text).split('|').map((part) => `<span class="phrase-unit">${escapeHtml(part)}</span>`).join('<wbr>')
|
||||
: escapeHtml(text);
|
||||
|
||||
const example = `let
|
||||
Port = Int & >= 1 & <= 65535;
|
||||
Service = {
|
||||
host = String;
|
||||
port = Port default 8080;
|
||||
};
|
||||
in
|
||||
{
|
||||
host = "127.0.0.1";
|
||||
} as Service;`;
|
||||
|
||||
const result = `{
|
||||
"host": "127.0.0.1",
|
||||
"port": 8080
|
||||
}`;
|
||||
---
|
||||
<ManualLayout title={copy.title} description={copy.description} locale={locale} frontpage>
|
||||
<section class="home-intro" aria-labelledby="home-title">
|
||||
<div class="home-intro-copy">
|
||||
<h1 id="home-title" class="phrased" set:html={phrase(copy.heroTitle)} />
|
||||
<p class="home-lead phrased" set:html={phrase(copy.heroLead)} />
|
||||
<div class="home-actions">
|
||||
<a class="button primary" href="/playground/">{copy.playground}</a>
|
||||
<a class="text-action" href="/docs/introduction/">{copy.introduction} <span aria-hidden="true">→</span></a>
|
||||
</div>
|
||||
<dl class="home-facts">
|
||||
{copy.facts.map(([label, value], index) => (
|
||||
<div>
|
||||
<dt>{label}</dt>
|
||||
<dd>{index === 0 ? <code>{value}</code> : value}</dd>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div class="home-code" aria-label={copy.exampleLabel}>
|
||||
<div class="code-caption">
|
||||
<span>service.dcdl</span>
|
||||
<span>DCDL</span>
|
||||
</div>
|
||||
<pre class="code-block"><code class="language-dcdl" set:html={highlightCode(example, 'dcdl')} /></pre>
|
||||
<div class="materialized-result">
|
||||
<span>{copy.materialize}</span>
|
||||
<pre><code>{result}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="home-section" aria-labelledby="model-title">
|
||||
<header class="section-heading">
|
||||
<h2 id="model-title" class="phrased" set:html={phrase(copy.modelTitle)} />
|
||||
</header>
|
||||
<div class="definition-list">
|
||||
{copy.definitions.map(([operator, title, description]) => (
|
||||
<article>
|
||||
<code>{operator}</code>
|
||||
<div>
|
||||
<h3 class="phrased" set:html={phrase(title)} />
|
||||
<p class="phrased" set:html={phrase(description)} />
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="home-section host-section" aria-labelledby="host-title">
|
||||
<header class="section-heading">
|
||||
<h2 id="host-title" class="phrased" set:html={phrase(copy.hostTitle)} />
|
||||
<p class="phrased" set:html={phrase(copy.hostDescription)} />
|
||||
</header>
|
||||
<ol class="evaluation-flow" aria-label={copy.flowLabel}>
|
||||
{copy.flow.map(([title, description], index) => (
|
||||
<li>
|
||||
<span>{index + 1}</span>
|
||||
<strong class="phrased" set:html={phrase(title)} />
|
||||
<small class="phrased" set:html={phrase(description)} />
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
<p class="section-link"><a href="/docs/embedding/">{copy.embedding} <span aria-hidden="true">→</span></a></p>
|
||||
</section>
|
||||
|
||||
<section class="home-section package-section" aria-labelledby="packages-title">
|
||||
<header class="section-heading">
|
||||
<h2 id="packages-title" class="phrased" set:html={phrase(copy.packagesTitle)} />
|
||||
</header>
|
||||
<div class="package-rows">
|
||||
<article>
|
||||
<div><code>decodal</code><span>Rust</span></div>
|
||||
<p class="phrased" set:html={phrase(copy.packages[0][0])} />
|
||||
<a href="https://crates.io/crates/decodal">crates.io</a>
|
||||
</article>
|
||||
<article>
|
||||
<div><code>decodal-wasm</code><span>JavaScript</span></div>
|
||||
<p class="phrased" set:html={phrase(copy.packages[1][0])} />
|
||||
<span class="package-links"><a href="https://jsr.io/@hare/decodal-wasm@0.3.0">JSR</a><a href="https://www.npmjs.com/package/decodal-wasm">npm</a></span>
|
||||
</article>
|
||||
<article>
|
||||
<div><code>decodal-lsp</code><span>Rust</span></div>
|
||||
<p class="phrased" set:html={phrase(copy.packages[2][0])} />
|
||||
<a href="https://crates.io/crates/decodal-lsp">crates.io</a>
|
||||
</article>
|
||||
<article>
|
||||
<div><code>decodal-codemirror</code><span>JavaScript</span></div>
|
||||
<p class="phrased" set:html={phrase(copy.packages[3][0])} />
|
||||
<span class="package-links"><a href="https://jsr.io/@hare/decodal-codemirror@0.3.0">JSR</a><a href="https://www.npmjs.com/package/decodal-codemirror">npm</a></span>
|
||||
</article>
|
||||
</div>
|
||||
<p class="section-link"><a href="/docs/components/">{copy.packagesLink} <span aria-hidden="true">→</span></a></p>
|
||||
</section>
|
||||
</ManualLayout>
|
||||
Reference in New Issue
Block a user