Add i18n support: per-locale catalogs, URL-driven locale, translated core and examples
CI / full-gate (push) Successful in 2m37s

This commit is contained in:
2026-08-03 22:37:27 +02:00
parent c30cd95ebd
commit 245d1ad5b5
93 changed files with 2480 additions and 464 deletions
@@ -11,28 +11,28 @@
-%>
<div class="form-page">
<% if (locals.created) { -%>
<%- include("partials/alert", { text: "Client registered.", tone: "pos" }) %>
<%- include("partials/alert", { text: t("admin.clients.createdNotice"), tone: "pos" }) %>
<% } -%>
<% if (locals.secret) { -%>
<section class="form-card" aria-labelledby="secret-h">
<h2 class="card-title" id="secret-h">Client secret</h2>
<p class="field-hint">Copy these now — the secret can't be shown again. Store them where the app reads its credentials.</p>
<div class="field"><label for="cid">Client ID</label><input class="input" id="cid" type="text" value="<%= c.id %>" readonly></div>
<div class="field"><label for="csecret">Client secret</label><input class="input" id="csecret" type="text" value="<%= locals.secret %>" readonly></div>
<h2 class="card-title" id="secret-h"><%= t("admin.clients.secret") %></h2>
<p class="field-hint"><%= t("admin.clients.secretHint") %></p>
<div class="field"><label for="cid"><%= t("admin.clients.column.id") %></label><input class="input" id="cid" type="text" value="<%= c.id %>" readonly></div>
<div class="field"><label for="csecret"><%= t("admin.clients.secret") %></label><input class="input" id="csecret" type="text" value="<%= locals.secret %>" readonly></div>
</section>
<% } -%>
<section class="form-card" aria-labelledby="client-h">
<h2 class="card-title" id="client-h"><%= c.name %></h2>
<dl class="detail-list">
<dt>Client ID</dt><dd><%= c.id %></dd>
<dt>Type</dt><dd><%= c.public ? "Public (PKCE)" : "Confidential" %></dd>
<dt>Consent</dt><dd><%= c.firstParty ? "First-party (auto-granted)" : "Shows the consent screen" %></dd>
<dt>Scopes</dt><dd><%= c.scopes.length ? c.scopes.join(" ") : "—" %></dd>
<dt>Redirect URIs</dt><dd><% if (c.redirectUris.length) { %><ul class="plain-list"><% c.redirectUris.forEach((u) => { %><li><%= u %></li><% }) %></ul><% } else { %>—<% } %></dd>
<dt><%= t("admin.clients.column.id") %></dt><dd><%= c.id %></dd>
<dt><%= t("admin.clients.column.type") %></dt><dd><%= c.public ? t("admin.clients.publicPkce") : t("admin.clients.confidential") %></dd>
<dt><%= t("admin.clients.consent.label") %></dt><dd><%= c.firstParty ? t("admin.clients.consent.firstParty") : t("admin.clients.consent.screen") %></dd>
<dt><%= t("admin.clients.field.scopes") %></dt><dd><%= c.scopes.length ? c.scopes.join(" ") : "—" %></dd>
<dt><%= t("admin.clients.field.redirectUris") %></dt><dd><% if (c.redirectUris.length) { %><ul class="plain-list"><% c.redirectUris.forEach((u) => { %><li><%= u %></li><% }) %></ul><% } else { %>—<% } %></dd>
</dl>
</section>
<section class="form-card admin-actions" aria-label="Client actions">
<p class="field-hint">To change a client, delete and re-register — this issues a new client ID and secret. The secret is shown only once, at registration.</p>
<a class="btn btn-danger" href="<%= del.action %>"><svg class="ico ico-sm" aria-hidden="true"><use href="#i-trash"/></svg>Delete client</a>
<section class="form-card admin-actions" aria-label="<%= t("admin.clients.title") %>">
<p class="field-hint"><%= t("admin.clients.rereg") %></p>
<a class="btn btn-danger" href="<%= del.action %>"><svg class="ico ico-sm" aria-hidden="true"><use href="#i-trash"/></svg><%= t("admin.clients.delete") %></a>
</section>
</div>