Synk fra arbeidsrepoet

This commit is contained in:
Vaultnord
2026-08-31 21:12:36 +00:00
parent c5dc020a5a
commit e0994317c6
3 changed files with 279 additions and 66 deletions
+15 -12
View File
@@ -17,15 +17,25 @@ ARG VW_DIGEST=
FROM vaultwarden/server:${VW_VERSION}@${VW_DIGEST} AS kilde FROM vaultwarden/server:${VW_VERSION}@${VW_DIGEST} AS kilde
FROM alpine:3.21@sha256:48b0309ca019d89d40f670aa1bc06e426dc0931948452e8491e3d65087abc07d AS sprak FROM alpine:3.21@sha256:48b0309ca019d89d40f670aa1bc06e426dc0931948452e8491e3d65087abc07d AS sprak
RUN apk add --no-cache jq RUN apk add --no-cache python3
COPY --from=kilde /web-vault/locales/nb/messages.json /nb.json COPY --from=kilde /web-vault/locales /locales
ARG LOCALE_DIR=vault-image/locale ARG LOCALE_DIR=vault-image/locale
COPY ${LOCALE_DIR}/nb.json /over.json COPY ${LOCALE_DIR}/nb.json /over.json
RUN jq -s '.[0] * .[1]' /nb.json /over.json > /merged.json \ COPY ${LOCALE_DIR}/sprak.py /sprak.py
&& jq -e '.vaultWelcomeDialogTitle.message | test("Velkommen")' /merged.json >/dev/null # 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} 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 # Ressursene ligger i brand/ i det offentlige repoet og i
# vault-image/brand/ i arbeidsrepoet. Byggekonteksten avgjør hvilken. # vault-image/brand/ i arbeidsrepoet. Byggekonteksten avgjør hvilken.
@@ -45,13 +55,6 @@ RUN set -eux; \
sed -i 's#</body>#<div id="vaultnord-footer">Driftet av <a href="https://vaultnord.no">Vaultnord</a> \&middot; <a href="/_vaultnord/kilde.html">kildekode og lisens</a></div></body>#' /web-vault/index.html; \ sed -i 's#</body>#<div id="vaultnord-footer">Driftet av <a href="https://vaultnord.no">Vaultnord</a> \&middot; <a href="/_vaultnord/kilde.html">kildekode og lisens</a></div></body>#' /web-vault/index.html; \
grep -q '_vaultnord/brand.css' /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 # 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 # oppstrøms endret noe, og pynten ville blitt stille borte. Da skal bygget
# stoppe her i stedet - se lista øverst i brand.css. # stoppe her i stedet - se lista øverst i brand.css.
+153 -54
View File
@@ -1,56 +1,155 @@
{ {
"vaultWelcomeDialogTitle": { "message": "Du er inne! Velkommen til Vaultnord" }, "vaultWelcomeDialogTitle": {
"vaultWelcomeDialogDescription": { "message": "Her lagrer du passordene og den personlige informasjonen din. Vi viser deg rundt." }, "message": "Du er inne! Velkommen til Vaultnord"
"vaultWelcomeDialogPrimaryCta": { "message": "Ta omvisningen" }, },
"vaultWelcomeDialogDismissCta": { "message": "Hopp over" }, "vaultWelcomeDialogDescription": {
"message": "Her lagrer du passordene og den personlige informasjonen din. Vi viser deg rundt."
"welcome": { "message": "Velkommen" }, },
"continue": { "message": "Fortsett" }, "vaultWelcomeDialogPrimaryCta": {
"skip": { "message": "Hopp over" }, "message": "Ta omvisningen"
"close": { "message": "Lukk" }, },
"next": { "message": "Neste" }, "vaultWelcomeDialogDismissCta": {
"back": { "message": "Tilbake" }, "message": "Hopp over"
"done": { "message": "Ferdig" }, },
"getStarted": { "message": "Kom i gang" }, "welcome": {
"learnMore": { "message": "Les mer" }, "message": "Velkommen"
},
"logInToBitwarden": { "message": "Logg inn i Vaultnord" }, "continue": {
"newToBitwarden": { "message": "Ny hos Vaultnord?" }, "message": "Fortsett"
"loggingInAs": { "message": "Logger inn som" }, },
"notYou": { "message": "Ikke deg?" }, "skip": {
"masterPass": { "message": "Hovedpassord" }, "message": "Hopp over"
"masterPassDesc": { "message": "Hovedpassordet låser opp hvelvet ditt. Det kan ikke gjenopprettes hvis du glemmer det." }, },
"unlock": { "message": "Lås opp" }, "close": {
"lock": { "message": "Lås" }, "message": "Lukk"
"loggedOut": { "message": "Logget ut" }, },
"next": {
"myVault": { "message": "Mitt hvelv" }, "message": "Neste"
"allItems": { "message": "Alle elementer" }, },
"favorites": { "message": "Favoritter" }, "back": {
"trash": { "message": "Papirkurv" }, "message": "Tilbake"
"addItem": { "message": "Legg til element" }, },
"editItem": { "message": "Rediger element" }, "done": {
"itemInformation": { "message": "Om elementet" }, "message": "Ferdig"
"noItemsInList": { "message": "Ingen elementer her ennå." }, },
"searchVault": { "message": "Søk i hvelvet" }, "getStarted": {
"copyValue": { "message": "Kopier" }, "message": "Kom i gang"
"toggleVisibility": { "message": "Vis eller skjul" }, },
"generatePassword": { "message": "Generer passord" }, "learnMore": {
"checkPassword": { "message": "Sjekk om passordet er lekket" }, "message": "Les mer"
},
"collections": { "message": "Samlinger" }, "logInToBitwarden": {
"members": { "message": "Medlemmer" }, "message": "Logg inn i Vaultnord"
"organizations": { "message": "Organisasjoner" }, },
"newOrganization": { "message": "Ny organisasjon" }, "newToBitwarden": {
"confirmMembers": { "message": "Bekreft medlemmer" }, "message": "Ny hos Vaultnord?"
"inviteMember": { "message": "Inviter medlem" }, },
"loggingInAs": {
"settings": { "message": "Innstillinger" }, "message": "Logger inn som"
"security": { "message": "Sikkerhet" }, },
"twoStepLogin": { "message": "To-trinns innlogging" }, "notYou": {
"changeMasterPassword": { "message": "Bytt hovedpassord" }, "message": "Ikke deg?"
"exportVault": { "message": "Eksporter hvelvet" }, },
"importData": { "message": "Importer data" }, "masterPass": {
"language": { "message": "Språk" }, "message": "Hovedpassord"
"helpAndFeedback": { "message": "Hjelp" } },
"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"
}
} }
+111
View File
@@ -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())