ALVA deployment config
Guidad Felsökning / test-och-bygg (push) Has been cancelled
Guidad Felsökning / tjanster (push) Has been cancelled
Guidad Felsökning / terraform (push) Has been cancelled
Guidad Felsökning / leverantorskedja (push) Has been cancelled
Guidad Felsökning / aterstallning (push) Has been cancelled
Guidad Felsökning / publicera (push) Has been cancelled
Guidad Felsökning / driftsatt (push) Has been cancelled
Guidad Felsökning / test-och-bygg (push) Has been cancelled
Guidad Felsökning / tjanster (push) Has been cancelled
Guidad Felsökning / terraform (push) Has been cancelled
Guidad Felsökning / leverantorskedja (push) Has been cancelled
Guidad Felsökning / aterstallning (push) Has been cancelled
Guidad Felsökning / publicera (push) Has been cancelled
Guidad Felsökning / driftsatt (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,237 @@
|
||||
// ALVA-SPEC-060 · Språk.
|
||||
//
|
||||
// ---- Två sorters text, och de behandlas olika ---------------------------
|
||||
//
|
||||
// GRÄNSSNITTSTEXT är etiketter, knappar, statusar och grindens hinder.
|
||||
// Den är ändlig, den ändras sällan, och en engelsk sträng som slinker
|
||||
// igenom till en tysk användare är irriterande men ofarlig.
|
||||
//
|
||||
// METODIKINNEHÅLL är instruktioner för arbete på ett fordon: "kontrollera
|
||||
// bromsvätskans kokpunkt", "säkerställ att fordonet är spänningslöst".
|
||||
// Där är en oöversatt eller halvöversatt sträng inte irriterande utan
|
||||
// farlig, och en maskinöversättning som ingen fackman läst är värre än
|
||||
// engelska — därför att engelska SYNS som ett annat språk, medan en
|
||||
// felaktig översättning ser ut som en instruktion.
|
||||
//
|
||||
// Modulen behandlar dem därför olika. Gränssnittstext faller tillbaka på
|
||||
// engelska tyst. Metodikinnehåll gör det ALDRIG tyst: det märks som
|
||||
// oöversatt, med språk och granskningsstatus, så att teknikern vet att
|
||||
// den läser något som inte är skrivet för dess marknad.
|
||||
//
|
||||
// ---- Källspråket är engelska --------------------------------------------
|
||||
//
|
||||
// Inte därför att engelska är bäst, utan därför att ALVA:s struktur redan
|
||||
// är engelsk och oföränderlig (ALVA-SPEC-001 §2). Ett system där
|
||||
// strukturen är engelsk och källtexten svensk har två källor att hålla
|
||||
// synkade, och den ena vinner alltid tyst.
|
||||
//
|
||||
// ---- Katalogen är stängd -------------------------------------------------
|
||||
//
|
||||
// Varje nyckel måste finnas i varje språk. Ett test låser det, av samma
|
||||
// skäl som händelseschemat är stängt: en saknad nyckel som tyst faller
|
||||
// tillbaka blir aldrig upptäckt, och blir aldrig översatt.
|
||||
//
|
||||
// ---- Och en del av den får INTE översättas ------------------------------
|
||||
//
|
||||
// ALVA-SPEC-001 §2 slår fast att fasnamn och statusord är strukturen och
|
||||
// skrivs likadant i varje land — precis som ett ISO-krav inte blir
|
||||
// "internationell standard 9001" på svenska. En tysk rapport och en
|
||||
// rumänsk ska visa samma fyra faser och samma statusord, så att en
|
||||
// revisor kan läsa båda utan att veta vilket språk verkstaden arbetar på.
|
||||
//
|
||||
// Det hade gått att skriva in dem oöversatta i varje katalog och låsa
|
||||
// dem med ett test. Det är svagare: en översättare som ser "Passed" i
|
||||
// den polska filen översätter den, och testet blir en tvist i stället
|
||||
// för en regel. I stället saknas nycklarna helt i översättningarna, och
|
||||
// `t()` hämtar dem alltid ur engelskan. Då finns det ingenting att
|
||||
// översätta fel.
|
||||
|
||||
import { EN } from "./en.mjs";
|
||||
import { SV } from "./sv.mjs";
|
||||
import { DE } from "./de.mjs";
|
||||
import { FR } from "./fr.mjs";
|
||||
import { ES } from "./es.mjs";
|
||||
import { IT } from "./it.mjs";
|
||||
import { PL } from "./pl.mjs";
|
||||
import { NL } from "./nl.mjs";
|
||||
import { PT } from "./pt.mjs";
|
||||
import { RO } from "./ro.mjs";
|
||||
|
||||
/**
|
||||
* Språken.
|
||||
*
|
||||
* Urvalet är EU:s största modersmål plus svenska, som är hemmamarknaden.
|
||||
* `granskat` säger om en fackman har läst igenom METODIKINNEHÅLLET på
|
||||
* språket — inte gränssnittet. Se filhuvudet: det är den skillnaden som
|
||||
* betyder något.
|
||||
*/
|
||||
export const SPRAK = [
|
||||
{ kod: "en", namn: "English", egetNamn: "English", granskat: true },
|
||||
{ kod: "de", namn: "German", egetNamn: "Deutsch", granskat: false },
|
||||
{ kod: "fr", namn: "French", egetNamn: "Français", granskat: false },
|
||||
{ kod: "es", namn: "Spanish", egetNamn: "Español", granskat: false },
|
||||
{ kod: "it", namn: "Italian", egetNamn: "Italiano", granskat: false },
|
||||
{ kod: "pl", namn: "Polish", egetNamn: "Polski", granskat: false },
|
||||
{ kod: "nl", namn: "Dutch", egetNamn: "Nederlands", granskat: false },
|
||||
{ kod: "pt", namn: "Portuguese", egetNamn: "Português", granskat: false },
|
||||
{ kod: "ro", namn: "Romanian", egetNamn: "Română", granskat: false },
|
||||
{ kod: "sv", namn: "Swedish", egetNamn: "Svenska", granskat: true },
|
||||
];
|
||||
|
||||
/** Standardspråket. Även källspråket. */
|
||||
export const STANDARD = "en";
|
||||
|
||||
export const KATALOGER = { en: EN, sv: SV, de: DE, fr: FR, es: ES, it: IT, pl: PL, nl: NL, pt: PT, ro: RO };
|
||||
|
||||
/**
|
||||
* Nyckelområden som är ALVA:s struktur och därför aldrig översätts.
|
||||
*
|
||||
* `fas.` — de fyra faserna. `status.` — statusorden. Se filhuvudet.
|
||||
*/
|
||||
export const INVARIANTA = ["fas.", "status."];
|
||||
|
||||
/** Om nyckeln tillhör strukturen och alltid ska hämtas ur engelskan. */
|
||||
export const arInvariant = (nyckel) => INVARIANTA.some((p) => String(nyckel).startsWith(p));
|
||||
|
||||
/**
|
||||
* Slår upp en gränssnittssträng.
|
||||
*
|
||||
* Invarianta nycklar hämtas alltid ur engelskan, oavsett språk och
|
||||
* oavsett vad en katalog råkar innehålla.
|
||||
*
|
||||
* Faller i övrigt tillbaka på engelska när nyckeln saknas i språket, och
|
||||
* på nyckeln själv när den saknas överallt — det sista ska aldrig hända
|
||||
* och fångas av testet, men en tom sträng i gränssnittet vore värre än
|
||||
* en synlig nyckel.
|
||||
*
|
||||
* `variabler` ersätter {namn} i strängen. Ingen ordning antas: språk
|
||||
* sätter satsdelar i olika ordning, och en översättning som tvingas följa
|
||||
* engelskans ordföljd blir fel svenska, tyska och polska på en gång.
|
||||
*/
|
||||
export function t(sprak, nyckel, variabler = {}) {
|
||||
const katalog = arInvariant(nyckel) ? EN : (KATALOGER[sprak] ?? EN);
|
||||
const rå = katalog[nyckel] ?? EN[nyckel] ?? nyckel;
|
||||
return String(rå).replace(/\{(\w+)\}/g, (_, n) => (n in variabler ? String(variabler[n]) : `{${n}}`));
|
||||
}
|
||||
|
||||
/** Bunden variant, så en vy slipper skicka språket i varje anrop. */
|
||||
export const oversattare = (sprak) => (nyckel, variabler) => t(sprak, nyckel, variabler);
|
||||
|
||||
/**
|
||||
* Vilka nycklar som saknas per språk.
|
||||
*
|
||||
* Används av testet — men är exporterad även för driften, som kan visa
|
||||
* täckningsgraden i stället för att påstå att ett språk är klart.
|
||||
*/
|
||||
export function saknade(katalog) {
|
||||
const nycklar = Object.keys(EN).filter((n) => !arInvariant(n));
|
||||
return nycklar.filter((n) => !(n in katalog) || !String(katalog[n]).trim());
|
||||
}
|
||||
|
||||
/**
|
||||
* Invarianta nycklar som en katalog felaktigt definierar.
|
||||
*
|
||||
* Tom lista är det normala. En träff betyder att någon har översatt
|
||||
* strukturen, och det ska stoppa bygget — inte tyst ignoreras av `t()`,
|
||||
* även om `t()` ignorerar det.
|
||||
*/
|
||||
export function otillatna(katalog) {
|
||||
return Object.keys(katalog).filter(arInvariant);
|
||||
}
|
||||
|
||||
/**
|
||||
* Nycklar som finns i katalogen men inte i engelskan.
|
||||
*
|
||||
* En sträng utan original. Den kan aldrig visas — `t()` slår upp mot
|
||||
* EN:s nycklar — så den är antingen ett stavfel eller en kvarleva.
|
||||
*/
|
||||
export function overflodiga(katalog) {
|
||||
return Object.keys(katalog).filter((n) => !arInvariant(n) && !(n in EN));
|
||||
}
|
||||
|
||||
/** Andel av gränssnittet som finns på språket. */
|
||||
export function tackning(kod) {
|
||||
const katalog = KATALOGER[kod];
|
||||
if (!katalog) return 0;
|
||||
const totalt = Object.keys(EN).filter((n) => !arInvariant(n)).length;
|
||||
return totalt === 0 ? 1 : (totalt - saknade(katalog).length) / totalt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Väljer språk ur det som finns att gå på.
|
||||
*
|
||||
* Ordningen är avsiktlig: användarens eget val väger tyngst, därefter
|
||||
* organisationens, därefter webbläsarens, sist engelska. Organisationen
|
||||
* går före webbläsaren därför att en verkstad i Tyskland med en polsk
|
||||
* tekniker ska kunna ha ett gemensamt dokumentationsspråk — rapporten ska
|
||||
* inte byta språk beroende på vem som råkade skriva raden.
|
||||
*/
|
||||
export function valjSprak({ anvandare, organisation, webblasare } = {}) {
|
||||
const kanda = new Set(SPRAK.map((s) => s.kod));
|
||||
const forsta = [anvandare, organisation, ...(webblasare ?? [])]
|
||||
.filter(Boolean)
|
||||
.map((k) => String(k).slice(0, 2).toLowerCase())
|
||||
.find((k) => kanda.has(k));
|
||||
return forsta ?? STANDARD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Besked om metodikinnehåll som inte är granskat på användarens språk.
|
||||
*
|
||||
* Returnerar null när allt är i sin ordning. Annars en text som säger
|
||||
* VAD som inte är granskat och VILKET språk texten faktiskt är på — inte
|
||||
* en allmän brasklapp, som ingen läser.
|
||||
*/
|
||||
export function metodikvarning(sprak) {
|
||||
const s = SPRAK.find((x) => x.kod === sprak);
|
||||
if (!s || s.granskat) return null;
|
||||
return t(sprak, "metodik.ogranskad", { sprak: s.egetNamn });
|
||||
}
|
||||
|
||||
/**
|
||||
* Ärendetyper som skrevs på svenska innan engelska blev källspråket.
|
||||
*
|
||||
* Ärendetypen är DATA: den ligger i händelseloggen och slår upp
|
||||
* regelpaketets extra krav. Ett byte av språk får därför inte göras tyst
|
||||
* — ett gammalt garantiärende vars typ heter "Garanti" hade annars inte
|
||||
* matchat "Warranty", fått noll extra krav, och släppts igenom grinden
|
||||
* med FÄRRE krav än när det öppnades. Ingenting i gränssnittet hade
|
||||
* visat det.
|
||||
*
|
||||
* Tabellen är därför en del av regeln, inte en bekvämlighet, och den tas
|
||||
* bort först när ingen logg innehåller de gamla värdena.
|
||||
*/
|
||||
export const ARENDETYP_ARV = {
|
||||
"Privat kund": "Private customer",
|
||||
Företagskund: "Business customer",
|
||||
Garanti: "Warranty",
|
||||
Försäkring: "Insurance",
|
||||
Reklamation: "Complaint",
|
||||
Begagnatgaranti: "Used-vehicle warranty",
|
||||
"Intern kvalitetskontroll": "Internal quality check",
|
||||
"Teknisk utredning": "Technical investigation",
|
||||
};
|
||||
|
||||
/** Ärendetypen i sin nuvarande form, oavsett vilket språk den skrevs i. */
|
||||
export const arendetypNu = (typ) => ARENDETYP_ARV[typ] ?? typ;
|
||||
|
||||
/**
|
||||
* Jakande svar på en säkerhetsspärr.
|
||||
*
|
||||
* Svaret på en ja/nej-fråga ligger i loggen som text, och spärrfrågorna
|
||||
* släpper bara igenom ett jakande svar. När knappen bytte från "Ja" till
|
||||
* "Yes" gick de två isär: klienten skrev "Yes", servern jämförde mot
|
||||
* "Ja". Utfallet blev fail-closed — högvoltsärenden gick inte att
|
||||
* avsluta alls — vilket är rätt riktning för ett fel att falla åt, men
|
||||
* det var en slump och inte en konstruktion.
|
||||
*
|
||||
* Det verkliga felet är att en säkerhetsspärr avgörs av en textsträng.
|
||||
* Rätt lösning är ett booleskt fält i händelsen; tills det finns står
|
||||
* båda formerna här, och de gäller alla språk samtidigt av samma skäl
|
||||
* som ordlistorna i ord.mjs gör det: den som svarar ska inte kunna
|
||||
* missas för att organisationens språkinställning var en annan.
|
||||
*/
|
||||
export const JAKANDE = new Set(["Yes", "Ja", "Oui", "Sí", "Si", "Sì", "Tak", "Da", "Sim", "Ja."]);
|
||||
|
||||
/** Är svaret jakande, oavsett vilket språk knappen stod på? */
|
||||
export const arJakande = (svar) => JAKANDE.has(String(svar ?? "").trim());
|
||||
Reference in New Issue
Block a user