@@ -1,5 +1,16 @@
|
||||
# Changelog
|
||||
|
||||
## 1.6.2
|
||||
|
||||
- Added a dedicated, long-form background page at `/background`.
|
||||
- Added localized legal and editorial explanations for German, English, French, Spanish, Italian, Dutch, Portuguese and Polish.
|
||||
- Explained the separate obligations in Article 50(1)–(5), with a specific section for articles, websites, images, audio and video.
|
||||
- Added a clear distinction between legal labelling duties and broader voluntary disclosure.
|
||||
- Added official links to the EU AI Act, the Commission guidelines of 20 July 2026, the Article 50 Q&A, the Code of Practice and the EU icons.
|
||||
- Changed all “Background” navigation links to the new internal page.
|
||||
- Added language switching, canonical URLs and `hreflang` metadata to the background page.
|
||||
- Added regression tests for all eight languages and navigation.
|
||||
|
||||
## 1.6.1
|
||||
|
||||
- Added a generator control for the declaration evidence basis: `selfDeclared`, `technicallyRecorded`, `signed`, and `verified`.
|
||||
|
||||
@@ -21,4 +21,4 @@ build:
|
||||
CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o bin/ai-disclosure ./cmd/server
|
||||
|
||||
docker-build:
|
||||
docker build -t ai-disclosure-standard:1.6.1-local .
|
||||
docker build -t ai-disclosure-standard:1.6.2-local .
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# AI Disclosure Standard 1.6.1
|
||||
# AI Disclosure Standard 1.6.2
|
||||
|
||||
Ein zustandsloser Go-Dienst für sichtbare und maschinenlesbare Erklärungen zur KI-Nutzung in Artikeln, Webseiten und einzelnen Inhaltsbestandteilen.
|
||||
|
||||
@@ -11,6 +11,7 @@ Ein zustandsloser Go-Dienst für sichtbare und maschinenlesbare Erklärungen zur
|
||||
- frei wählbare Nachweisgrundlage: Selbsterklärung, technisch protokolliert, signiert oder verifiziert;
|
||||
- Deutsch, Englisch, Französisch, Spanisch, Italienisch, Niederländisch, Portugiesisch und Polnisch;
|
||||
- Sprachumschalter direkt auf der Ergebnisseite;
|
||||
- mehrsprachige Hintergrundseite zu Artikel 50 des EU AI Act, Kennzeichnungspflichten und freiwilliger Transparenz;
|
||||
- optionale lizenzierte Funktionen für eigene Texte und Badge-Darstellung;
|
||||
- Offline-, Hybrid- und Online-Prüfung über die eigenständige Universal License Platform;
|
||||
- Health-, Readiness- und Prometheus-Endpunkte;
|
||||
@@ -27,6 +28,7 @@ Danach:
|
||||
```text
|
||||
Generator: http://localhost:8080/
|
||||
Produktseite: http://localhost:8080/product
|
||||
Hintergrund: http://localhost:8080/background
|
||||
Healthcheck: http://localhost:8080/healthz
|
||||
Funktionen: http://localhost:8080/v1/capabilities
|
||||
```
|
||||
@@ -150,6 +152,7 @@ Weitere Einzelheiten stehen in [`docs/LICENSE-INTEGRATION.md`](docs/LICENSE-INTE
|
||||
```text
|
||||
GET /badge/{preset}.svg
|
||||
GET /v1/badge.svg
|
||||
GET /background
|
||||
GET /declaration
|
||||
GET /v1/declaration.json
|
||||
POST /v1/validate
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
services:
|
||||
app:
|
||||
build: .
|
||||
image: ai-disclosure-standard:1.6.1-local
|
||||
image: ai-disclosure-standard:1.6.2-local
|
||||
environment:
|
||||
BASE_URL: "${BASE_URL:-http://localhost:8080}"
|
||||
PUBLIC_NAME: "${PUBLIC_NAME:-AI Usage Disclosure}"
|
||||
|
||||
@@ -29,7 +29,7 @@ spec:
|
||||
type: RuntimeDefault
|
||||
containers:
|
||||
- name: app
|
||||
image: ghcr.io/REPLACE_ME/ai-disclosure-standard:1.6.1
|
||||
image: ghcr.io/REPLACE_ME/ai-disclosure-standard:1.6.2
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- name: http
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
version: "3.9"
|
||||
services:
|
||||
app:
|
||||
image: ghcr.io/REPLACE_ME/ai-disclosure-standard:1.6.1
|
||||
image: ghcr.io/REPLACE_ME/ai-disclosure-standard:1.6.2
|
||||
environment:
|
||||
BASE_URL: https://ai.example.org
|
||||
PUBLIC_NAME: AI Usage Disclosure
|
||||
|
||||
@@ -16,7 +16,7 @@ Initialization is performed in `internal/app/server.go`. The product ID and embe
|
||||
```go
|
||||
licenses := licenseclient.New(ctx, licenseclient.Config{
|
||||
Product: "ai-disclosure-standard",
|
||||
ClientVersion: "1.6.1",
|
||||
ClientVersion: "1.6.2",
|
||||
Token: cfg.LicenseToken,
|
||||
TrustStore: trustStore,
|
||||
BaseURL: cfg.BaseURL,
|
||||
|
||||
@@ -119,7 +119,7 @@ Anfrage:
|
||||
"baseUrl": "https://ai.example.org",
|
||||
"host": "ai.example.org",
|
||||
"instanceId": "production-eu-1",
|
||||
"clientVersion": "1.6.1"
|
||||
"clientVersion": "1.6.2"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
const (
|
||||
ProductID = "ai-disclosure-standard"
|
||||
ProductVersion = "1.6.1"
|
||||
ProductVersion = "1.6.2"
|
||||
FeatureCustomText = "custom_text"
|
||||
FeatureCustomBadge = "custom_badge"
|
||||
FeatureWhiteLabel = "white_label"
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/b1tsblog/ai-disclosure-standard/internal/background"
|
||||
"github.com/b1tsblog/ai-disclosure-standard/internal/badge"
|
||||
"github.com/b1tsblog/ai-disclosure-standard/internal/declaration"
|
||||
"github.com/b1tsblog/ai-disclosure-standard/internal/i18n"
|
||||
@@ -71,6 +72,7 @@ type pageData struct {
|
||||
CustomText bool
|
||||
CustomBadge bool
|
||||
Marketing marketing.Page
|
||||
Background background.Page
|
||||
LanguageLinks []languageLink
|
||||
DefaultLanguageURL string
|
||||
}
|
||||
@@ -108,6 +110,7 @@ func (s *Server) routes() {
|
||||
fileServer := http.FileServer(http.FS(staticFS))
|
||||
s.mux.HandleFunc("GET /", s.handleIndex)
|
||||
s.mux.HandleFunc("GET /product", s.handleMarketing)
|
||||
s.mux.HandleFunc("GET /background", s.handleBackground)
|
||||
s.mux.HandleFunc("GET /install", s.handleMarketingAlias)
|
||||
s.mux.Handle("GET /static/", http.StripPrefix("/static/", cacheStatic(fileServer)))
|
||||
s.mux.HandleFunc("GET /badge/{file}", s.handlePresetBadge)
|
||||
@@ -148,6 +151,34 @@ func (s *Server) handleIndex(w http.ResponseWriter, r *http.Request) {
|
||||
s.renderHTML(w, "index.html", data)
|
||||
}
|
||||
|
||||
func (s *Server) handleBackground(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path != "/background" {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
lang := s.language(r)
|
||||
languageLinks, defaultLanguageURL := s.staticLanguageLinks("/background", lang)
|
||||
data := pageData{
|
||||
Name: s.cfg.PublicName, BaseURL: s.cfg.BaseURL, ContactURL: s.cfg.ContactURL,
|
||||
Lang: lang, Languages: i18n.Languages(), Background: background.Build(lang),
|
||||
License: s.licenses.Status(), LanguageLinks: languageLinks, DefaultLanguageURL: defaultLanguageURL,
|
||||
}
|
||||
s.renderHTML(w, "background.html", data)
|
||||
}
|
||||
|
||||
func (s *Server) staticLanguageLinks(path, current string) ([]languageLink, string) {
|
||||
links := make([]languageLink, 0, len(i18n.Languages()))
|
||||
for _, language := range i18n.Languages() {
|
||||
values := url.Values{"lang": []string{language.Code}}
|
||||
relative := path + "?" + values.Encode()
|
||||
links = append(links, languageLink{
|
||||
Code: language.Code, Name: language.Name, URL: relative,
|
||||
AbsoluteURL: s.cfg.BaseURL + relative, Current: language.Code == current,
|
||||
})
|
||||
}
|
||||
return links, s.cfg.BaseURL + path
|
||||
}
|
||||
|
||||
func (s *Server) handleMarketing(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path != "/product" {
|
||||
http.NotFound(w, r)
|
||||
|
||||
@@ -221,3 +221,53 @@ func TestSignedAssuranceExplainsScope(t *testing.T) {
|
||||
t.Fatal("signed assurance scope is not explained")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackgroundPageGerman(t *testing.T) {
|
||||
r := httptest.NewRequest(http.MethodGet, "/background?lang=de", nil)
|
||||
w := httptest.NewRecorder()
|
||||
testHandler(t).ServeHTTP(w, r)
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("status %d: %s", w.Code, w.Body.String())
|
||||
}
|
||||
body := w.Body.String()
|
||||
for _, expected := range []string{
|
||||
"Warum KI-Nutzung gekennzeichnet wird",
|
||||
"Artikel 50 gilt ab 2. August 2026",
|
||||
"keine allgemeine Kennzeichnungspflicht",
|
||||
"KI-generierter Text zu Angelegenheiten von öffentlichem Interesse",
|
||||
"Leitlinien zu Transparenzpflichten",
|
||||
`hreflang="en"`,
|
||||
`id="background-language"`,
|
||||
} {
|
||||
if !strings.Contains(body, expected) {
|
||||
t.Fatalf("background page missing %q", expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackgroundPageFrenchNegotiation(t *testing.T) {
|
||||
r := httptest.NewRequest(http.MethodGet, "/background", nil)
|
||||
r.Header.Set("Accept-Language", "fr-FR,fr;q=0.9,en;q=0.8")
|
||||
w := httptest.NewRecorder()
|
||||
testHandler(t).ServeHTTP(w, r)
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("status %d: %s", w.Code, w.Body.String())
|
||||
}
|
||||
if !strings.Contains(w.Body.String(), "Pourquoi signaler l’utilisation de l’IA") {
|
||||
t.Fatal("missing French background content")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNavigationUsesInternalBackgroundPage(t *testing.T) {
|
||||
for _, path := range []string{"/?lang=de", "/product?lang=de"} {
|
||||
r := httptest.NewRequest(http.MethodGet, path, nil)
|
||||
w := httptest.NewRecorder()
|
||||
testHandler(t).ServeHTTP(w, r)
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("%s returned %d", path, w.Code)
|
||||
}
|
||||
if !strings.Contains(w.Body.String(), `href="/background?lang=de"`) {
|
||||
t.Fatalf("%s does not link to the internal background page", path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,13 +183,13 @@ func Build(lang, productName, baseURL, salesURL, contactURL string, prices Price
|
||||
# Adjust BASE_URL and PUBLIC_NAME in .env
|
||||
docker compose up -d --build
|
||||
curl -fsS http://localhost:8080/readyz`},
|
||||
{ID: "docker", Title: copy.InstallTitles[1], Summary: copy.InstallSummaries[1], Code: `docker build -t ai-disclosure-standard:1.6.1 .
|
||||
{ID: "docker", Title: copy.InstallTitles[1], Summary: copy.InstallSummaries[1], Code: `docker build -t ai-disclosure-standard:1.6.2 .
|
||||
docker run -d --name ai-disclosure \
|
||||
-p 8080:8080 \
|
||||
-e BASE_URL=https://ai.example.org \
|
||||
-e PUBLIC_NAME="AI Usage Disclosure" \
|
||||
--read-only --tmpfs /tmp \
|
||||
ai-disclosure-standard:1.6.1`},
|
||||
ai-disclosure-standard:1.6.2`},
|
||||
{ID: "kubernetes", Title: copy.InstallTitles[2], Summary: copy.InstallSummaries[2], Code: `# Image, Domain und TLS-Secret in deploy/kubernetes.yaml ersetzen
|
||||
kubectl create secret generic ai-disclosure-license \
|
||||
--from-literal=token='...'
|
||||
@@ -230,14 +230,14 @@ PUBLIC_NAME="AI Usage Disclosure" \
|
||||
|
||||
func configurationDescriptions(lang string) [8]string {
|
||||
values := map[string][8]string{
|
||||
"de": {"Bind-Adresse des HTTP-Servers.", "Öffentliche Basis-URL ohne abschließenden Slash.", "Produkt- oder Seitentitel.", "Link zur Hintergrund- oder Kontaktseite.", "Rückfallsprache der Ausgabe.", "Vom Anbieter signierter Lizenz-Token.", "Mindestmodus: offline, hybrid oder online.", "Optionaler zentraler Prüfserver für Hybrid- und Online-Modus."},
|
||||
"en": {"HTTP server bind address.", "Public base URL without a trailing slash.", "Product or site title.", "Background or contact page.", "Fallback output language.", "Vendor-signed licence token.", "Minimum mode: offline, hybrid or online.", "Optional central verification server for hybrid and online mode."},
|
||||
"fr": {"Adresse d'écoute du serveur HTTP.", "URL publique sans barre oblique finale.", "Nom du produit ou du site.", "Page de contexte ou de contact.", "Langue de secours.", "Jeton de licence signé par l'éditeur.", "Mode minimal : offline, hybrid ou online.", "Serveur central facultatif pour les modes hybride et en ligne."},
|
||||
"es": {"Dirección de escucha del servidor HTTP.", "URL pública sin barra final.", "Nombre del producto o sitio.", "Página de contexto o contacto.", "Idioma de reserva.", "Token de licencia firmado por el proveedor.", "Modo mínimo: offline, hybrid u online.", "Servidor central opcional para los modos híbrido y en línea."},
|
||||
"it": {"Indirizzo di ascolto del server HTTP.", "URL pubblica senza slash finale.", "Nome del prodotto o sito.", "Pagina di contesto o contatto.", "Lingua di fallback.", "Token di licenza firmato dal fornitore.", "Modalità minima: offline, hybrid o online.", "Server centrale opzionale per modalità ibrida e online."},
|
||||
"nl": {"Luisteradres van de HTTP-server.", "Publieke basis-URL zonder afsluitende slash.", "Product- of sitenaam.", "Achtergrond- of contactpagina.", "Terugvaltaal.", "Door de leverancier ondertekend licentietoken.", "Minimale modus: offline, hybrid of online.", "Optionele centrale controleserver voor hybride en online modus."},
|
||||
"pt": {"Endereço de escuta do servidor HTTP.", "URL pública sem barra final.", "Nome do produto ou site.", "Página de contexto ou contacto.", "Idioma de fallback.", "Token de licença assinado pelo fornecedor.", "Modo mínimo: offline, hybrid ou online.", "Servidor central opcional para os modos híbrido e online."},
|
||||
"pl": {"Adres nasłuchiwania serwera HTTP.", "Publiczny bazowy URL bez końcowego ukośnika.", "Nazwa produktu lub witryny.", "Strona informacji lub kontaktu.", "Język zapasowy.", "Token licencji podpisany przez dostawcę.", "Minimalny tryb: offline, hybrid lub online.", "Opcjonalny centralny serwer weryfikacji dla trybu hybrydowego i online."},
|
||||
"de": {"Bind-Adresse des HTTP-Servers.", "Öffentliche Basis-URL ohne abschließenden Slash.", "Produkt- oder Seitentitel.", "Optionale externe Kontakt- oder Projektseite.", "Rückfallsprache der Ausgabe.", "Vom Anbieter signierter Lizenz-Token.", "Mindestmodus: offline, hybrid oder online.", "Optionaler zentraler Prüfserver für Hybrid- und Online-Modus."},
|
||||
"en": {"HTTP server bind address.", "Public base URL without a trailing slash.", "Product or site title.", "Optional external contact or project page.", "Fallback output language.", "Vendor-signed licence token.", "Minimum mode: offline, hybrid or online.", "Optional central verification server for hybrid and online mode."},
|
||||
"fr": {"Adresse d'écoute du serveur HTTP.", "URL publique sans barre oblique finale.", "Nom du produit ou du site.", "Page externe facultative de contact ou de projet.", "Langue de secours.", "Jeton de licence signé par l'éditeur.", "Mode minimal : offline, hybrid ou online.", "Serveur central facultatif pour les modes hybride et en ligne."},
|
||||
"es": {"Dirección de escucha del servidor HTTP.", "URL pública sin barra final.", "Nombre del producto o sitio.", "Página externa opcional de contacto o del proyecto.", "Idioma de reserva.", "Token de licencia firmado por el proveedor.", "Modo mínimo: offline, hybrid u online.", "Servidor central opcional para los modos híbrido y en línea."},
|
||||
"it": {"Indirizzo di ascolto del server HTTP.", "URL pubblica senza slash finale.", "Nome del prodotto o sito.", "Pagina esterna facoltativa di contatto o del progetto.", "Lingua di fallback.", "Token di licenza firmato dal fornitore.", "Modalità minima: offline, hybrid o online.", "Server centrale opzionale per modalità ibrida e online."},
|
||||
"nl": {"Luisteradres van de HTTP-server.", "Publieke basis-URL zonder afsluitende slash.", "Product- of sitenaam.", "Optionele externe contact- of projectpagina.", "Terugvaltaal.", "Door de leverancier ondertekend licentietoken.", "Minimale modus: offline, hybrid of online.", "Optionele centrale controleserver voor hybride en online modus."},
|
||||
"pt": {"Endereço de escuta do servidor HTTP.", "URL pública sem barra final.", "Nome do produto ou site.", "Página externa opcional de contacto ou do projeto.", "Idioma de fallback.", "Token de licença assinado pelo fornecedor.", "Modo mínimo: offline, hybrid ou online.", "Servidor central opcional para os modos híbrido e online."},
|
||||
"pl": {"Adres nasłuchiwania serwera HTTP.", "Publiczny bazowy URL bez końcowego ukośnika.", "Nazwa produktu lub witryny.", "Opcjonalna zewnętrzna strona kontaktowa lub projektu.", "Język zapasowy.", "Token licencji podpisany przez dostawcę.", "Minimalny tryb: offline, hybrid lub online.", "Opcjonalny centralny serwer weryfikacji dla trybu hybrydowego i online."},
|
||||
}
|
||||
if descriptions, ok := values[normalize(lang)]; ok {
|
||||
return descriptions
|
||||
@@ -372,7 +372,7 @@ func german() copySet {
|
||||
InstallTitles: [5]string{"Docker Compose", "Docker CLI", "Kubernetes", "Docker Swarm", "Direkt mit Go"},
|
||||
InstallSummaries: [5]string{"Lokaler oder einzelner Server mit deklarativer Konfiguration.", "Image selbst bauen und als gehärteten Container starten.", "Drei Replikate, Probes, Rolling Updates, HPA und PodDisruptionBudget.", "Mehrere Replikate mit Docker-nativem Orchestrator betreiben.", "Für Entwicklung, Tests oder ein natives Systemd-Deployment."},
|
||||
ConfigTitle: "Wichtige Umgebungsvariablen", ConfigVariable: "Variable", ConfigDefault: "Standard", ConfigMeaning: "Bedeutung",
|
||||
ConfigDescriptions: [8]string{"Bind-Adresse des HTTP-Servers.", "Öffentliche Basis-URL ohne abschließenden Slash.", "Produkt- oder Seitentitel.", "Link zur Hintergrund- oder Kontaktseite.", "Ziel für Pro- und Vertriebsanfragen.", "Rückfallsprache der Ausgabe.", "Öffentlicher Ed25519-Schlüssel für Pro.", "Signierter und optional domaingebundener Pro-Token."},
|
||||
ConfigDescriptions: [8]string{"Bind-Adresse des HTTP-Servers.", "Öffentliche Basis-URL ohne abschließenden Slash.", "Produkt- oder Seitentitel.", "Optionale externe Kontakt- oder Projektseite.", "Ziel für Pro- und Vertriebsanfragen.", "Rückfallsprache der Ausgabe.", "Öffentlicher Ed25519-Schlüssel für Pro.", "Signierter und optional domaingebundener Pro-Token."},
|
||||
FAQEyebrow: "FAQ", FAQTitle: "Häufige Fragen",
|
||||
FAQs: []FAQ{
|
||||
{"Ist die Community-Ausgabe eingeschränkt?", "Die Standard-Presets, acht Sprachen, SVG, Erklärungseiten, JSON-LD, Validator und alle Deployment-Dateien sind vollständig nutzbar. Nur eigene Texte, Farben und kommerzielle Zusatzleistungen benötigen Pro."},
|
||||
@@ -408,7 +408,7 @@ func english() copySet {
|
||||
PlanCTA: [5]string{"Start free", "Contact Pro", "Contact Publisher", "Contact Agency", "Contact sales"}, EnterprisePrice: "Custom",
|
||||
InstallEyebrow: "Deployment", InstallTitle: "Production-ready in a few commands.", InstallLead: "Every option runs the same Go binary. Community and Pro differ only through optional licence secrets.", CopyLabel: "Copy", CopiedLabel: "Copied",
|
||||
InstallTitles: [5]string{"Docker Compose", "Docker CLI", "Kubernetes", "Docker Swarm", "Run with Go"}, InstallSummaries: [5]string{"Declarative setup for a local or single-server deployment.", "Build the image and run a hardened container directly.", "Three replicas, probes, rolling updates, HPA and a PodDisruptionBudget.", "Operate several replicas with Docker's native orchestrator.", "For development, tests or a native systemd deployment."},
|
||||
ConfigTitle: "Important environment variables", ConfigVariable: "Variable", ConfigDefault: "Default", ConfigMeaning: "Purpose", ConfigDescriptions: [8]string{"HTTP server bind address.", "Public base URL without a trailing slash.", "Product or site title.", "Background or contact page.", "Destination for Pro and sales enquiries.", "Fallback output language.", "Public Ed25519 key for Pro verification.", "Signed and optionally domain-bound Pro token."},
|
||||
ConfigTitle: "Important environment variables", ConfigVariable: "Variable", ConfigDefault: "Default", ConfigMeaning: "Purpose", ConfigDescriptions: [8]string{"HTTP server bind address.", "Public base URL without a trailing slash.", "Product or site title.", "Optional external contact or project page.", "Destination for Pro and sales enquiries.", "Fallback output language.", "Public Ed25519 key for Pro verification.", "Signed and optionally domain-bound Pro token."},
|
||||
FAQEyebrow: "FAQ", FAQTitle: "Common questions", FAQs: []FAQ{{"Is Community artificially limited?", "No. Standard presets, eight languages, SVG, declaration pages, JSON-LD, validation and deployment files are fully usable. Only custom presentation and commercial services require Pro."}, {"Does Pro depend on a licensing server?", "No. Every replica verifies the Ed25519 signature locally, preserving availability and supporting isolated networks."}, {"Can I place the service behind a CDN?", "Yes. Badge and manifest responses are deterministic and include ETag and cache-control headers."}, {"Does this automatically make a site legally compliant?", "No. The service provides technical declarations and integrations; it does not replace legal review or editorial responsibility."}},
|
||||
FinalTitle: "Start open. Add branding only where you need it.", FinalLead: "The generator works without registration. Pro is available for custom copy, colours and licensed domains.", FinalPrimary: "Open generator", FinalSecondary: "Discuss Pro", Footer: "Open AI usage disclosure standard · Not legal advice.",
|
||||
}
|
||||
@@ -437,7 +437,7 @@ func french() copySet {
|
||||
c.CopyLabel, c.CopiedLabel = "Copier", "Copié"
|
||||
c.InstallSummaries = [5]string{"Configuration déclarative locale ou mono-serveur.", "Construire l'image et lancer directement un conteneur renforcé.", "Trois répliques, probes, rolling updates, HPA et PodDisruptionBudget.", "Plusieurs répliques avec l'orchestrateur natif Docker.", "Pour le développement, les tests ou un service systemd natif."}
|
||||
c.ConfigTitle, c.ConfigVariable, c.ConfigDefault, c.ConfigMeaning = "Variables d'environnement importantes", "Variable", "Valeur par défaut", "Rôle"
|
||||
c.ConfigDescriptions = [8]string{"Adresse d'écoute HTTP.", "URL publique sans barre oblique finale.", "Nom du produit ou du site.", "Page de contexte ou de contact.", "Destination des demandes Pro et commerciales.", "Langue de repli.", "Clé publique Ed25519 pour Pro.", "Jeton Pro signé et éventuellement lié au domaine."}
|
||||
c.ConfigDescriptions = [8]string{"Adresse d'écoute HTTP.", "URL publique sans barre oblique finale.", "Nom du produit ou du site.", "Page externe facultative de contact ou de projet.", "Destination des demandes Pro et commerciales.", "Langue de repli.", "Clé publique Ed25519 pour Pro.", "Jeton Pro signé et éventuellement lié au domaine."}
|
||||
c.FAQEyebrow, c.FAQTitle = "FAQ", "Questions fréquentes"
|
||||
c.FAQs = []FAQ{{"Community est-elle limitée artificiellement ?", "Non. Les modèles, huit langues, SVG, pages, JSON-LD, validation et fichiers de déploiement sont complets. Seule la personnalisation commerciale nécessite Pro."}, {"Pro dépend-il d'un serveur de licence ?", "Non. Chaque réplique vérifie localement la signature Ed25519."}, {"Puis-je utiliser un CDN ?", "Oui. Les badges et manifestes sont déterministes et utilisent ETag et Cache-Control."}, {"Cela garantit-il la conformité juridique ?", "Non. Le service fournit une infrastructure technique et ne remplace ni l'analyse juridique ni la responsabilité éditoriale."}}
|
||||
c.FinalTitle, c.FinalLead, c.FinalPrimary, c.FinalSecondary = "Commencez ouvert. Ajoutez votre marque uniquement si nécessaire.", "Le générateur fonctionne sans inscription. Pro ajoute les textes, couleurs et domaines personnalisés.", "Ouvrir le générateur", "Discuter de Pro"
|
||||
@@ -468,7 +468,7 @@ func spanish() copySet {
|
||||
c.CopyLabel, c.CopiedLabel = "Copiar", "Copiado"
|
||||
c.InstallSummaries = [5]string{"Configuración declarativa local o de un servidor.", "Construye la imagen y ejecuta un contenedor endurecido.", "Tres réplicas, probes, rolling updates, HPA y PodDisruptionBudget.", "Varias réplicas con el orquestador nativo de Docker.", "Para desarrollo, pruebas o un despliegue nativo con systemd."}
|
||||
c.ConfigTitle, c.ConfigVariable, c.ConfigDefault, c.ConfigMeaning = "Variables de entorno importantes", "Variable", "Valor por defecto", "Uso"
|
||||
c.ConfigDescriptions = [8]string{"Dirección de escucha HTTP.", "URL pública sin barra final.", "Título del producto o sitio.", "Página de contexto o contacto.", "Destino de consultas Pro y comerciales.", "Idioma de respaldo.", "Clave pública Ed25519 para Pro.", "Token Pro firmado y opcionalmente ligado a dominios."}
|
||||
c.ConfigDescriptions = [8]string{"Dirección de escucha HTTP.", "URL pública sin barra final.", "Título del producto o sitio.", "Página externa opcional de contacto o del proyecto.", "Destino de consultas Pro y comerciales.", "Idioma de respaldo.", "Clave pública Ed25519 para Pro.", "Token Pro firmado y opcionalmente ligado a dominios."}
|
||||
c.FAQEyebrow, c.FAQTitle = "FAQ", "Preguntas frecuentes"
|
||||
c.FAQs = []FAQ{{"¿Community está limitada artificialmente?", "No. Los preajustes, ocho idiomas, SVG, páginas, JSON-LD, validación y archivos de despliegue son completos. Solo la personalización comercial requiere Pro."}, {"¿Pro depende de un servidor de licencias?", "No. Cada réplica verifica localmente la firma Ed25519."}, {"¿Puedo usar un CDN?", "Sí. Las respuestas son deterministas e incluyen ETag y Cache-Control."}, {"¿Garantiza cumplimiento legal?", "No. Es infraestructura técnica y no sustituye revisión jurídica ni responsabilidad editorial."}}
|
||||
c.FinalTitle, c.FinalLead, c.FinalPrimary, c.FinalSecondary = "Empieza abierto. Añade marca solo cuando la necesites.", "El generador funciona sin registro. Pro añade textos, colores y dominios personalizados.", "Abrir generador", "Hablar de Pro"
|
||||
@@ -499,7 +499,7 @@ func italian() copySet {
|
||||
c.CopyLabel, c.CopiedLabel = "Copia", "Copiato"
|
||||
c.InstallSummaries = [5]string{"Configurazione dichiarativa locale o su server singolo.", "Costruisci l'immagine ed esegui un container hardened.", "Tre repliche, probe, rolling update, HPA e PodDisruptionBudget.", "Più repliche con l'orchestratore nativo Docker.", "Per sviluppo, test o deployment nativo systemd."}
|
||||
c.ConfigTitle, c.ConfigVariable, c.ConfigDefault, c.ConfigMeaning = "Variabili d'ambiente importanti", "Variabile", "Default", "Scopo"
|
||||
c.ConfigDescriptions = [8]string{"Indirizzo di ascolto HTTP.", "URL pubblico senza slash finale.", "Titolo del prodotto o sito.", "Pagina di contesto o contatto.", "Destinazione delle richieste Pro e commerciali.", "Lingua di fallback.", "Chiave pubblica Ed25519 per Pro.", "Token Pro firmato e opzionalmente legato ai domini."}
|
||||
c.ConfigDescriptions = [8]string{"Indirizzo di ascolto HTTP.", "URL pubblico senza slash finale.", "Titolo del prodotto o sito.", "Pagina esterna facoltativa di contatto o del progetto.", "Destinazione delle richieste Pro e commerciali.", "Lingua di fallback.", "Chiave pubblica Ed25519 per Pro.", "Token Pro firmato e opzionalmente legato ai domini."}
|
||||
c.FAQEyebrow, c.FAQTitle = "FAQ", "Domande frequenti"
|
||||
c.FAQs = []FAQ{{"Community è limitata artificialmente?", "No. Preset, otto lingue, SVG, pagine, JSON-LD, validazione e file di deployment sono completi. Solo la personalizzazione commerciale richiede Pro."}, {"Pro dipende da un server di licenze?", "No. Ogni replica verifica localmente la firma Ed25519."}, {"Posso usare un CDN?", "Sì. Badge e manifesti sono deterministici e includono ETag e Cache-Control."}, {"Garantisce conformità legale?", "No. Fornisce infrastruttura tecnica e non sostituisce la revisione legale o la responsabilità editoriale."}}
|
||||
c.FinalTitle, c.FinalLead, c.FinalPrimary, c.FinalSecondary = "Inizia aperto. Aggiungi il brand solo quando serve.", "Il generatore funziona senza registrazione. Pro aggiunge testi, colori e domini personalizzati.", "Apri generatore", "Parla di Pro"
|
||||
@@ -530,7 +530,7 @@ func dutch() copySet {
|
||||
c.CopyLabel, c.CopiedLabel = "Kopiëren", "Gekopieerd"
|
||||
c.InstallSummaries = [5]string{"Declaratieve setup voor lokaal of één server.", "Bouw het image en start een hardened container.", "Drie replicas, probes, rolling updates, HPA en PodDisruptionBudget.", "Meerdere replicas met Docker Swarm.", "Voor ontwikkeling, tests of een native systemd-deployment."}
|
||||
c.ConfigTitle, c.ConfigVariable, c.ConfigDefault, c.ConfigMeaning = "Belangrijke omgevingsvariabelen", "Variabele", "Standaard", "Doel"
|
||||
c.ConfigDescriptions = [8]string{"HTTP-luisteradres.", "Publieke basis-URL zonder afsluitende slash.", "Product- of sitetitel.", "Achtergrond- of contactpagina.", "Bestemming voor Pro- en verkoopvragen.", "Fallbacktaal.", "Publieke Ed25519-sleutel voor Pro.", "Ondertekende en optioneel domeingebonden Pro-token."}
|
||||
c.ConfigDescriptions = [8]string{"HTTP-luisteradres.", "Publieke basis-URL zonder afsluitende slash.", "Product- of sitetitel.", "Optionele externe contact- of projectpagina.", "Bestemming voor Pro- en verkoopvragen.", "Fallbacktaal.", "Publieke Ed25519-sleutel voor Pro.", "Ondertekende en optioneel domeingebonden Pro-token."}
|
||||
c.FAQEyebrow, c.FAQTitle = "FAQ", "Veelgestelde vragen"
|
||||
c.FAQs = []FAQ{{"Is Community kunstmatig beperkt?", "Nee. Presets, acht talen, SVG, pagina's, JSON-LD, validatie en deploymentbestanden zijn volledig. Alleen commercieel maatwerk vereist Pro."}, {"Heeft Pro een licentieserver nodig?", "Nee. Elke replica controleert de Ed25519-handtekening lokaal."}, {"Kan dit achter een CDN?", "Ja. Badges en manifesten zijn deterministisch en gebruiken ETag en Cache-Control."}, {"Garandeert dit juridische naleving?", "Nee. Het is technische infrastructuur en vervangt geen juridische beoordeling of redactionele verantwoordelijkheid."}}
|
||||
c.FinalTitle, c.FinalLead, c.FinalPrimary, c.FinalSecondary = "Begin open. Voeg huisstijl toe waar nodig.", "De generator werkt zonder registratie. Pro voegt eigen teksten, kleuren en domeinen toe.", "Open generator", "Pro bespreken"
|
||||
@@ -561,7 +561,7 @@ func portuguese() copySet {
|
||||
c.CopyLabel, c.CopiedLabel = "Copiar", "Copiado"
|
||||
c.InstallSummaries = [5]string{"Configuração declarativa local ou num único servidor.", "Construa a imagem e execute um contentor hardened.", "Três réplicas, probes, rolling updates, HPA e PodDisruptionBudget.", "Várias réplicas com Docker Swarm.", "Para desenvolvimento, testes ou deployment nativo com systemd."}
|
||||
c.ConfigTitle, c.ConfigVariable, c.ConfigDefault, c.ConfigMeaning = "Variáveis de ambiente importantes", "Variável", "Padrão", "Objetivo"
|
||||
c.ConfigDescriptions = [8]string{"Endereço de escuta HTTP.", "URL pública sem barra final.", "Título do produto ou site.", "Página de contexto ou contacto.", "Destino para pedidos Pro e comerciais.", "Idioma de fallback.", "Chave pública Ed25519 para Pro.", "Token Pro assinado e opcionalmente ligado a domínios."}
|
||||
c.ConfigDescriptions = [8]string{"Endereço de escuta HTTP.", "URL pública sem barra final.", "Título do produto ou site.", "Página externa opcional de contacto ou do projeto.", "Destino para pedidos Pro e comerciais.", "Idioma de fallback.", "Chave pública Ed25519 para Pro.", "Token Pro assinado e opcionalmente ligado a domínios."}
|
||||
c.FAQEyebrow, c.FAQTitle = "FAQ", "Perguntas frequentes"
|
||||
c.FAQs = []FAQ{{"Community é limitada artificialmente?", "Não. Presets, oito idiomas, SVG, páginas, JSON-LD, validação e ficheiros de deployment estão completos. Apenas a personalização comercial exige Pro."}, {"Pro depende de um servidor de licenças?", "Não. Cada réplica valida localmente a assinatura Ed25519."}, {"Posso usar um CDN?", "Sim. Badges e manifestos são determinísticos e incluem ETag e Cache-Control."}, {"Isto garante conformidade legal?", "Não. É infraestrutura técnica e não substitui análise jurídica ou responsabilidade editorial."}}
|
||||
c.FinalTitle, c.FinalLead, c.FinalPrimary, c.FinalSecondary = "Comece aberto. Adicione a marca apenas quando necessário.", "O gerador funciona sem registo. Pro adiciona textos, cores e domínios personalizados.", "Abrir gerador", "Falar sobre Pro"
|
||||
@@ -592,7 +592,7 @@ func polish() copySet {
|
||||
c.CopyLabel, c.CopiedLabel = "Kopiuj", "Skopiowano"
|
||||
c.InstallSummaries = [5]string{"Deklaratywna konfiguracja lokalna lub na jednym serwerze.", "Zbuduj obraz i uruchom utwardzony kontener.", "Trzy repliki, probes, rolling updates, HPA i PodDisruptionBudget.", "Wiele replik z Docker Swarm.", "Do developmentu, testów lub natywnego wdrożenia systemd."}
|
||||
c.ConfigTitle, c.ConfigVariable, c.ConfigDefault, c.ConfigMeaning = "Ważne zmienne środowiskowe", "Zmienna", "Domyślnie", "Znaczenie"
|
||||
c.ConfigDescriptions = [8]string{"Adres nasłuchiwania HTTP.", "Publiczny URL bazowy bez końcowego ukośnika.", "Nazwa produktu lub strony.", "Strona informacji lub kontaktu.", "Cel zapytań Pro i sprzedażowych.", "Język zapasowy.", "Publiczny klucz Ed25519 dla Pro.", "Podpisany i opcjonalnie związany z domeną token Pro."}
|
||||
c.ConfigDescriptions = [8]string{"Adres nasłuchiwania HTTP.", "Publiczny URL bazowy bez końcowego ukośnika.", "Nazwa produktu lub strony.", "Opcjonalna zewnętrzna strona kontaktowa lub projektu.", "Cel zapytań Pro i sprzedażowych.", "Język zapasowy.", "Publiczny klucz Ed25519 dla Pro.", "Podpisany i opcjonalnie związany z domeną token Pro."}
|
||||
c.FAQEyebrow, c.FAQTitle = "FAQ", "Częste pytania"
|
||||
c.FAQs = []FAQ{{"Czy Community jest sztucznie ograniczona?", "Nie. Presety, osiem języków, SVG, strony, JSON-LD, walidacja i pliki wdrożeniowe są kompletne. Tylko personalizacja komercyjna wymaga Pro."}, {"Czy Pro wymaga serwera licencji?", "Nie. Każda replika lokalnie weryfikuje podpis Ed25519."}, {"Czy mogę użyć CDN?", "Tak. Plakietki i manifesty są deterministyczne i używają ETag oraz Cache-Control."}, {"Czy to gwarantuje zgodność prawną?", "Nie. To infrastruktura techniczna, która nie zastępuje analizy prawnej ani odpowiedzialności redakcyjnej."}}
|
||||
c.FinalTitle, c.FinalLead, c.FinalPrimary, c.FinalSecondary = "Zacznij otwarcie. Dodaj markę tylko tam, gdzie jej potrzebujesz.", "Generator działa bez rejestracji. Pro dodaje własne teksty, kolory i domeny.", "Otwórz generator", "Porozmawiaj o Pro"
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
openapi: 3.1.0
|
||||
info:
|
||||
title: AI Usage Disclosure API
|
||||
version: 1.6.1
|
||||
version: 1.6.2
|
||||
description: Stateless multilingual badge, declaration and validation API with optional licensed presentation capabilities.
|
||||
servers:
|
||||
- url: https://ai.example.org
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
(() => {
|
||||
document.getElementById('marketing-language')?.addEventListener('change', event => {
|
||||
const next = new URL(window.location.href);
|
||||
next.searchParams.set('lang', event.target.value);
|
||||
window.location.assign(next.toString());
|
||||
document.querySelectorAll('#marketing-language, [data-language-switch]').forEach(select => {
|
||||
select.addEventListener('change', event => {
|
||||
const next = new URL(window.location.href);
|
||||
next.searchParams.set('lang', event.target.value);
|
||||
window.location.assign(next.toString());
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll('.copy-code').forEach(button => {
|
||||
|
||||
@@ -37,3 +37,8 @@
|
||||
.metadata-facts dt,.metadata-facts dd{padding:14px 16px}
|
||||
.metadata-facts dt{background:#f3f1ec;border-right:1px solid var(--line)}
|
||||
@media(max-width:760px){.metadata-facts div{grid-template-columns:1fr}.metadata-facts dt{border-right:0;border-bottom:1px solid var(--line)}}
|
||||
|
||||
/* Legal and editorial background page */
|
||||
.background-page{background:var(--bg)}.background-header nav a[aria-current="page"]{font-weight:900;text-decoration:underline;text-underline-offset:6px}.background-main{max-width:1180px;width:min(1180px,calc(100% - 40px));margin:0 auto;padding:0}.background-hero{display:grid;grid-template-columns:minmax(0,1.45fr) minmax(320px,.75fr);gap:clamp(30px,7vw,92px);align-items:end;padding:clamp(58px,9vw,126px) 0 72px}.background-hero-copy h1{max-width:900px;font-size:clamp(3.2rem,7vw,7.2rem);line-height:.91;letter-spacing:-.07em;margin:.18em 0}.background-hero-copy .lead{max-width:820px}.legal-status{padding:28px;border:1px solid var(--line);border-top:5px solid #7c3aed;border-radius:18px;background:var(--surface);box-shadow:var(--shadow)}.legal-status h2{font-size:clamp(1.55rem,3vw,2.35rem);letter-spacing:-.04em;margin:.35rem 0 .75rem}.legal-status p:last-child{color:var(--muted);line-height:1.7}.background-toc{display:grid;grid-template-columns:220px 1fr;gap:34px;padding:28px 0 50px;border-top:1px solid var(--line);border-bottom:1px solid var(--line)}.background-toc .eyebrow{margin:.3rem 0}.background-toc ol{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px 28px;margin:0;padding-left:1.4rem}.background-toc li{padding:5px 0;color:var(--muted)}.background-toc a{color:var(--ink);font-weight:750;text-underline-offset:4px}.background-article{margin-top:6px}.background-section{display:grid;grid-template-columns:92px minmax(0,1fr);gap:30px;padding:88px 0;border-bottom:1px solid var(--line);scroll-margin-top:30px}.section-number{margin:8px 0 0;font:800 .78rem ui-monospace,SFMono-Regular,Menlo,monospace;letter-spacing:.14em;color:var(--muted)}.background-section h2{font-size:clamp(2rem,4.6vw,4.4rem);line-height:1;letter-spacing:-.055em;margin:0 0 26px}.prose-section>div{max-width:900px}.prose-section>div>p{font-size:clamp(1.06rem,1.45vw,1.28rem);line-height:1.78;color:#383838}.section-heading.compact{max-width:900px;margin-bottom:38px}.section-heading.compact p{font-size:1.08rem;line-height:1.7;color:var(--muted)}.legal-card-grid,.principle-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px}.legal-card,.principle-card{padding:27px;border:1px solid var(--line);border-radius:16px;background:var(--surface)}.legal-card h3,.principle-card h3{font-size:1.45rem;letter-spacing:-.035em;margin:.35rem 0 .8rem}.legal-card p:last-child,.principle-card p:last-child{margin-bottom:0;color:var(--muted);line-height:1.68}.scenario-list{border:1px solid var(--line);border-radius:17px;overflow:hidden;background:var(--surface)}.scenario-row{display:grid;grid-template-columns:minmax(180px,.32fr) minmax(0,1fr);gap:28px;padding:28px;border-bottom:1px solid var(--line)}.scenario-row:last-child{border-bottom:0}.scenario-row h3{font-size:1.35rem;letter-spacing:-.025em;margin:0 0 8px}.scenario-row p{margin:0;color:var(--muted);line-height:1.65}.scenario-status{display:inline-block;padding:7px 11px;border:1px solid #d8d1f5;border-radius:999px;background:#f4f1ff;color:#4f2aa8;font-size:.74rem;font-weight:850;letter-spacing:.035em}.project-scope-list{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:0 30px;margin:34px 0 0;padding:0;list-style:none;border-top:1px solid var(--line)}.project-scope-list li{position:relative;padding:16px 12px 16px 28px;border-bottom:1px solid var(--line);font-weight:700}.project-scope-list li::before{content:"✓";position:absolute;left:2px;color:#5b35b5}.source-list{display:grid;gap:10px}.source-card{display:flex;align-items:center;justify-content:space-between;gap:24px;padding:22px 24px;border:1px solid var(--line);border-radius:14px;background:var(--surface);color:var(--ink);text-decoration:none;transition:transform .15s ease,border-color .15s ease}.source-card:hover{transform:translateY(-2px);border-color:#777}.source-card span:first-child{display:grid;gap:5px}.source-card strong{font-size:1.04rem}.source-card small{color:var(--muted);line-height:1.5}.source-card span:last-child{font-size:1.35rem}.background-disclaimer{margin:58px 92px;padding:30px 34px;border:1px solid #d8d1f5;border-left:5px solid #7c3aed;border-radius:15px;background:#f7f4ff}.background-disclaimer h2{font-size:1.4rem;margin:0 0 8px}.background-disclaimer p{margin:0;line-height:1.7;color:#49415d}.background-cta{display:flex;align-items:center;justify-content:space-between;gap:34px;margin:70px 0 100px;padding:clamp(34px,6vw,68px);border-radius:24px;background:#171717;color:#fff}.background-cta h2{font-size:clamp(2rem,4vw,4rem);letter-spacing:-.055em;margin:0 0 12px}.background-cta p{max-width:720px;margin:0;color:#c8c8c8;font-size:1.08rem}.background-cta .button{flex:0 0 auto;background:#fff;color:#111;border-color:#fff}
|
||||
@media(max-width:900px){.background-hero{grid-template-columns:1fr;align-items:start}.legal-status{max-width:720px}.background-toc{grid-template-columns:1fr}.background-toc ol{grid-template-columns:repeat(2,minmax(0,1fr))}.background-section{grid-template-columns:56px minmax(0,1fr);gap:16px}.background-disclaimer{margin-left:56px;margin-right:0}.background-cta{align-items:flex-start;flex-direction:column}}
|
||||
@media(max-width:650px){.background-main{width:min(100% - 28px,1180px);padding:0}.background-hero{padding-top:46px}.background-hero-copy h1{font-size:clamp(3rem,16vw,5rem)}.background-toc ol{grid-template-columns:1fr}.background-section{grid-template-columns:1fr;padding:62px 0}.section-number{margin:0}.legal-card-grid,.principle-grid,.project-scope-list{grid-template-columns:1fr}.scenario-row{grid-template-columns:1fr;gap:14px;padding:22px}.background-disclaimer{margin:42px 0;padding:24px}.background-cta{margin-bottom:72px}}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<script type="application/ld+json">{{.JSONLD}}</script>
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-header"><a class="brand" href="/?lang={{.Lang}}">{{.Name}}</a><nav><a href="/?lang={{.Lang}}">{{index .Text "nav_generator"}}</a><a href="{{.ManifestURL}}">JSON-LD</a></nav></header>
|
||||
<header class="site-header"><a class="brand" href="/?lang={{.Lang}}">{{.Name}}</a><nav><a href="/?lang={{.Lang}}">{{index .Text "nav_generator"}}</a><a href="/background?lang={{.Lang}}">{{index .Text "nav_background"}}</a><a href="{{.ManifestURL}}">JSON-LD</a></nav></header>
|
||||
<main class="declaration-page">
|
||||
<a class="back" href="/?lang={{.Lang}}">← {{index .Text "back"}}</a>
|
||||
<article class="declaration-card">
|
||||
@@ -54,7 +54,7 @@
|
||||
<div class="notice"><strong>{{index .Text "transparency_label"}}</strong> {{index .Text "transparency_text"}}</div>
|
||||
</article>
|
||||
</main>
|
||||
<footer><span><a href="{{.ManifestURL}}">{{index .Text "manifest"}}</a></span><span><a href="{{.ContactURL}}">{{index .Text "nav_background"}}</a></span></footer>
|
||||
<footer><span><a href="{{.ManifestURL}}">{{index .Text "manifest"}}</a></span><span><a href="/background?lang={{.Lang}}">{{index .Text "nav_background"}}</a></span></footer>
|
||||
</body>
|
||||
</html>
|
||||
{{end}}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<body>
|
||||
<header class="site-header">
|
||||
<a class="brand" href="/?lang={{.Lang}}">{{.Name}}</a>
|
||||
<nav><a href="/product?lang={{.Lang}}">{{index .Text "nav_product"}}</a><a href="#generator">{{index .Text "nav_generator"}}</a><a href="#api">{{index .Text "nav_api"}}</a><a href="{{.ContactURL}}">{{index .Text "nav_background"}}</a></nav>
|
||||
<nav><a href="/product?lang={{.Lang}}">{{index .Text "nav_product"}}</a><a href="#generator">{{index .Text "nav_generator"}}</a><a href="#api">{{index .Text "nav_api"}}</a><a href="/background?lang={{.Lang}}">{{index .Text "nav_background"}}</a></nav>
|
||||
<label class="language-switch"><span class="sr-only">{{index .Text "field_language"}}</span><select id="page-language">
|
||||
{{range .Languages}}<option value="{{.Code}}" {{if eq $.Lang .Code}}selected{{end}}>{{.Name}}</option>{{end}}
|
||||
</select></label>
|
||||
@@ -132,7 +132,7 @@
|
||||
<article><p class="eyebrow">HA</p><h2>{{index .Text "api_stateless_title"}}</h2><p>{{index .Text "api_stateless_desc"}}</p></article>
|
||||
</section>
|
||||
</main>
|
||||
<footer><span>AI Usage Disclosure · App 1.6.1 · Schema 1.1 · {{.License.Edition}}</span><span>{{index .Text "footer_no_legal"}}</span></footer>
|
||||
<footer><span>AI Usage Disclosure · App 1.6.2 · Schema 1.1 · {{.License.Edition}}</span><span>{{index .Text "footer_no_legal"}}</span></footer>
|
||||
<script>window.APP_CONFIG={{.AppConfig}};</script>
|
||||
<script src="/static/app.js" defer></script>
|
||||
</body>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<a href="#features">{{.Marketing.NavFeatures}}</a>
|
||||
<a href="#install">{{.Marketing.NavInstall}}</a>
|
||||
<a href="/?lang={{.Lang}}#generator">{{.Marketing.NavGenerator}}</a>
|
||||
<a href="{{.ContactURL}}">{{.Marketing.NavBackground}}</a>
|
||||
<a href="/background?lang={{.Lang}}">{{.Marketing.NavBackground}}</a>
|
||||
</nav>
|
||||
<label class="language-switch"><span class="sr-only">{{.Marketing.LanguageLabel}}</span><select id="marketing-language">
|
||||
{{range .Languages}}<option value="{{.Code}}" {{if eq $.Lang .Code}}selected{{end}}>{{.Name}}</option>{{end}}
|
||||
@@ -119,7 +119,7 @@
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer><span>{{.Marketing.Footer}}</span><span>App 1.6 · Schema 1.1 · {{.License.Edition}}</span></footer>
|
||||
<footer><span>{{.Marketing.Footer}}</span><span>App 1.6.2 · Schema 1.1 · {{.License.Edition}}</span></footer>
|
||||
<script src="/static/marketing.js" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user