From e0994317c6dd86e6bb6e26e1bcebaee7d6f36f0d Mon Sep 17 00:00:00 2001 From: Vaultnord Date: Mon, 31 Aug 2026 21:12:36 +0000 Subject: [PATCH] Synk fra arbeidsrepoet --- Dockerfile | 27 ++++--- locale/nb.json | 207 +++++++++++++++++++++++++++++++++++------------- locale/sprak.py | 111 ++++++++++++++++++++++++++ 3 files changed, 279 insertions(+), 66 deletions(-) create mode 100644 locale/sprak.py diff --git a/Dockerfile b/Dockerfile index 8a85229..cc6858f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,15 +17,25 @@ ARG VW_DIGEST= FROM vaultwarden/server:${VW_VERSION}@${VW_DIGEST} AS kilde FROM alpine:3.21@sha256:48b0309ca019d89d40f670aa1bc06e426dc0931948452e8491e3d65087abc07d AS sprak -RUN apk add --no-cache jq -COPY --from=kilde /web-vault/locales/nb/messages.json /nb.json +RUN apk add --no-cache python3 +COPY --from=kilde /web-vault/locales /locales ARG LOCALE_DIR=vault-image/locale COPY ${LOCALE_DIR}/nb.json /over.json -RUN jq -s '.[0] * .[1]' /nb.json /over.json > /merged.json \ - && jq -e '.vaultWelcomeDialogTitle.message | test("Velkommen")' /merged.json >/dev/null +COPY ${LOCALE_DIR}/sprak.py /sprak.py +# Navnet byttes i verdiene, ikke i nøklene. En sed over hele fila døpte om +# atten oppslagsord, og da fant klienten dem ikke lenger - hele onboardingen +# forsvant i stillhet. Kontrollene i sprak.py stopper bygget hvis det gjentar seg. +RUN set -eux; \ + for f in /locales/*/messages.json; do \ + case "$f" in \ + */nb/messages.json) python3 /sprak.py "$f" Vaultnord /over.json ;; \ + *) python3 /sprak.py "$f" Vaultnord ;; \ + esac; \ + done; \ + python3 /sprak.py --sjekk /locales/nb/messages.json FROM vaultwarden/server:${VW_VERSION}@${VW_DIGEST} -COPY --from=sprak /merged.json /web-vault/locales/nb/messages.json +COPY --from=sprak /locales /web-vault/locales # Ressursene ligger i brand/ i det offentlige repoet og i # vault-image/brand/ i arbeidsrepoet. Byggekonteksten avgjør hvilken. @@ -45,13 +55,6 @@ RUN set -eux; \ sed -i 's###' /web-vault/index.html; \ grep -q '_vaultnord/brand.css' /web-vault/index.html; \ \ - # Web-klienten sier "Bitwarden" i grensesnittet - i velkomstskjermen, i - # hjelpetekster og i knappene. Vi bytter navnet i språkfilene. URL-er går - # klar: de skriver bitwarden.com med liten forbokstav. - find /web-vault/locales -name messages.json -exec \ - sed -i 's/Bitwarden/Vaultnord/g' {} +; \ - grep -q 'Vaultnord' /web-vault/locales/nb/messages.json; \ - \ # Brandingen henger på navn i web-klienten. Finnes de ikke lenger, har # oppstrøms endret noe, og pynten ville blitt stille borte. Da skal bygget # stoppe her i stedet - se lista øverst i brand.css. diff --git a/locale/nb.json b/locale/nb.json index e1ad19a..cb85d54 100644 --- a/locale/nb.json +++ b/locale/nb.json @@ -1,56 +1,155 @@ { - "vaultWelcomeDialogTitle": { "message": "Du er inne! Velkommen til Vaultnord" }, - "vaultWelcomeDialogDescription": { "message": "Her lagrer du passordene og den personlige informasjonen din. Vi viser deg rundt." }, - "vaultWelcomeDialogPrimaryCta": { "message": "Ta omvisningen" }, - "vaultWelcomeDialogDismissCta": { "message": "Hopp over" }, - - "welcome": { "message": "Velkommen" }, - "continue": { "message": "Fortsett" }, - "skip": { "message": "Hopp over" }, - "close": { "message": "Lukk" }, - "next": { "message": "Neste" }, - "back": { "message": "Tilbake" }, - "done": { "message": "Ferdig" }, - "getStarted": { "message": "Kom i gang" }, - "learnMore": { "message": "Les mer" }, - - "logInToBitwarden": { "message": "Logg inn i Vaultnord" }, - "newToBitwarden": { "message": "Ny hos Vaultnord?" }, - "loggingInAs": { "message": "Logger inn som" }, - "notYou": { "message": "Ikke deg?" }, - "masterPass": { "message": "Hovedpassord" }, - "masterPassDesc": { "message": "Hovedpassordet låser opp hvelvet ditt. Det kan ikke gjenopprettes hvis du glemmer det." }, - "unlock": { "message": "Lås opp" }, - "lock": { "message": "Lås" }, - "loggedOut": { "message": "Logget ut" }, - - "myVault": { "message": "Mitt hvelv" }, - "allItems": { "message": "Alle elementer" }, - "favorites": { "message": "Favoritter" }, - "trash": { "message": "Papirkurv" }, - "addItem": { "message": "Legg til element" }, - "editItem": { "message": "Rediger element" }, - "itemInformation": { "message": "Om elementet" }, - "noItemsInList": { "message": "Ingen elementer her ennå." }, - "searchVault": { "message": "Søk i hvelvet" }, - "copyValue": { "message": "Kopier" }, - "toggleVisibility": { "message": "Vis eller skjul" }, - "generatePassword": { "message": "Generer passord" }, - "checkPassword": { "message": "Sjekk om passordet er lekket" }, - - "collections": { "message": "Samlinger" }, - "members": { "message": "Medlemmer" }, - "organizations": { "message": "Organisasjoner" }, - "newOrganization": { "message": "Ny organisasjon" }, - "confirmMembers": { "message": "Bekreft medlemmer" }, - "inviteMember": { "message": "Inviter medlem" }, - - "settings": { "message": "Innstillinger" }, - "security": { "message": "Sikkerhet" }, - "twoStepLogin": { "message": "To-trinns innlogging" }, - "changeMasterPassword": { "message": "Bytt hovedpassord" }, - "exportVault": { "message": "Eksporter hvelvet" }, - "importData": { "message": "Importer data" }, - "language": { "message": "Språk" }, - "helpAndFeedback": { "message": "Hjelp" } + "vaultWelcomeDialogTitle": { + "message": "Du er inne! Velkommen til Vaultnord" + }, + "vaultWelcomeDialogDescription": { + "message": "Her lagrer du passordene og den personlige informasjonen din. Vi viser deg rundt." + }, + "vaultWelcomeDialogPrimaryCta": { + "message": "Ta omvisningen" + }, + "vaultWelcomeDialogDismissCta": { + "message": "Hopp over" + }, + "welcome": { + "message": "Velkommen" + }, + "continue": { + "message": "Fortsett" + }, + "skip": { + "message": "Hopp over" + }, + "close": { + "message": "Lukk" + }, + "next": { + "message": "Neste" + }, + "back": { + "message": "Tilbake" + }, + "done": { + "message": "Ferdig" + }, + "getStarted": { + "message": "Kom i gang" + }, + "learnMore": { + "message": "Les mer" + }, + "logInToBitwarden": { + "message": "Logg inn i Vaultnord" + }, + "newToBitwarden": { + "message": "Ny hos Vaultnord?" + }, + "loggingInAs": { + "message": "Logger inn som" + }, + "notYou": { + "message": "Ikke deg?" + }, + "masterPass": { + "message": "Hovedpassord" + }, + "masterPassDesc": { + "message": "Hovedpassordet låser opp hvelvet ditt. Det kan ikke gjenopprettes hvis du glemmer det." + }, + "unlock": { + "message": "Lås opp" + }, + "lock": { + "message": "Lås" + }, + "loggedOut": { + "message": "Logget ut" + }, + "myVault": { + "message": "Mitt hvelv" + }, + "allItems": { + "message": "Alle elementer" + }, + "favorites": { + "message": "Favoritter" + }, + "trash": { + "message": "Papirkurv" + }, + "addItem": { + "message": "Legg til element" + }, + "editItem": { + "message": "Rediger element" + }, + "itemInformation": { + "message": "Om elementet" + }, + "noItemsInList": { + "message": "Ingen elementer her ennå." + }, + "searchVault": { + "message": "Søk i hvelvet" + }, + "copyValue": { + "message": "Kopier" + }, + "toggleVisibility": { + "message": "Vis eller skjul" + }, + "generatePassword": { + "message": "Generer passord" + }, + "checkPassword": { + "message": "Sjekk om passordet er lekket" + }, + "collections": { + "message": "Samlinger" + }, + "members": { + "message": "Medlemmer" + }, + "organizations": { + "message": "Organisasjoner" + }, + "newOrganization": { + "message": "Ny organisasjon" + }, + "confirmMembers": { + "message": "Bekreft medlemmer" + }, + "inviteMember": { + "message": "Inviter medlem" + }, + "settings": { + "message": "Innstillinger" + }, + "security": { + "message": "Sikkerhet" + }, + "twoStepLogin": { + "message": "To-trinns innlogging" + }, + "changeMasterPassword": { + "message": "Bytt hovedpassord" + }, + "exportVault": { + "message": "Eksporter hvelvet" + }, + "importData": { + "message": "Importer data" + }, + "language": { + "message": "Språk" + }, + "helpAndFeedback": { + "message": "Hjelp" + }, + "emailPlaceholder": { + "message": "navn@firma.no" + }, + "emailPlaceholderMultiple": { + "message": "navn@firma.no, kollega@firma.no" + } } diff --git a/locale/sprak.py b/locale/sprak.py new file mode 100644 index 0000000..0a59e62 --- /dev/null +++ b/locale/sprak.py @@ -0,0 +1,111 @@ +"""Norske tekster og vårt navn inn i web-klientens språkfiler. + +Erstatter en `sed 's/Bitwarden/Vaultnord/g'` over hele fila. Den byttet navn +også i JSON-nøklene, og da slutter klienten å finne strengen - atten tekster +forsvant i stillhet, blant dem hele onboardingen. Samtidig slapp atten andre +unna, fordi de skriver navnet med liten forbokstav midt i en setning. + +Reglene her: + + * Nøkler røres aldri. De er klientens oppslagsord. + * Bare verdier byttes, og bare som helt ord. + * URL-er står urørt. bitwarden.com er deres adresse, ikke vår. + * Noen tekster handler faktisk om Bitwarden sine egne produkter - nettleser- + utvidelsen, Authenticator, Families. Å kalle dem våre ville vært å påstå at + vi eier noe andre har laget. +""" + +import json +import re +import sys + +# Tekster som virkelig handler om Bitwarden, og som skal fortsette å gjøre det. +BEHOLD = { + "twoStepContinueToBitwardenUrlTitle", + "twoStepContinueToBitwardenUrlDesc", + "openBitwardenExtension", + "contactBitwardenSupport", + "moreProductsFromBitwarden", + "moreFromBitwarden", + "sponsoredBitwardenFamilies", + "sponsorshipFreeBitwardenFamilies", + "acceptBitwardenFamiliesHelp", + "youHaveBitwardenPremium", + "startYour7DayFreeTrialOfBitwarden", + "startYour7DayFreeTrialOfBitwardenFor", + "startYour7DayFreeTrialOfBitwardenSecretsManagerFor", + "toHostBitwardenOnYourOwnServer", +} + +URL = re.compile(r"https?://[^\s\"<>]+|[\w.-]+@[\w.-]+|bitwarden\.com", re.I) +ORD = re.compile(r"\bbitwarden\b", re.I) + + +def bytt(tekst: str, navn: str) -> str: + """Bytter navnet i en tekst, men lar adresser stå.""" + vern = [] + + def gjem(m): + vern.append(m.group(0)) + return f"\x00{len(vern) - 1}\x00" + + trygg = URL.sub(gjem, tekst) + trygg = ORD.sub(navn, trygg) + return re.sub(r"\x00(\d+)\x00", lambda m: vern[int(m.group(1))], trygg) + + +def rebrand(data: dict, navn: str) -> dict: + ut = {} + for nokkel, verdi in data.items(): + if nokkel in BEHOLD or not isinstance(verdi, dict): + ut[nokkel] = verdi + continue + ny = dict(verdi) + if isinstance(ny.get("message"), str): + ny["message"] = bytt(ny["message"], navn) + ut[nokkel] = ny # nøkkelen selv står urørt + return ut + + +# Det bygget skal stoppe på. Hver linje er en feil vi har hatt. +KONTROLLER = [ + ("vaultWelcomeDialogTitle", "Velkommen", True, "norsk velkomsttekst mangler"), + ("logInToBitwarden", "Vaultnord", True, "verdien er ikke byttet"), + ("turnOnNewDeviceLoginProtectionModalDesc", "bitwarden", False, + "liten forbokstav slapp unna"), + ("openBitwardenExtension", "Bitwarden", True, "unntaket ble overkjørt"), +] + + +def sjekk(sti: str) -> int: + data = json.load(open(sti)) + feil = [] + for nokkel, ord_, skal_finnes, hvorfor in KONTROLLER: + if nokkel not in data: + feil.append(f"{nokkel}: nøkkelen er borte - er den døpt om?") + continue + har = ord_.lower() in data[nokkel].get("message", "").lower() + if har != skal_finnes: + feil.append(f"{nokkel}: {hvorfor}") + if "bitwarden.com" not in json.dumps(data): + feil.append("adressene til bitwarden.com skal stå urørt") + for f in feil: + print(f" FEIL {f}", file=sys.stderr) + if not feil: + print(" språkfilene ser riktige ut") + return 1 if feil else 0 + + +def main() -> int: + if sys.argv[1] == "--sjekk": + return sjekk(sys.argv[2]) + sti, navn = sys.argv[1], sys.argv[2] + over = json.load(open(sys.argv[3])) if len(sys.argv) > 3 else {} + data = json.load(open(sti)) + data.update(over) # våre egne tekster først + json.dump(rebrand(data, navn), open(sti, "w"), ensure_ascii=False, indent=1) + return 0 + + +if __name__ == "__main__": + sys.exit(main())