@cosmicdrift/kumiko-locale-de 0.258.1 → 0.260.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/__tests__/en-catalog.ts +10 -0
- package/src/__tests__/formal.test.ts +53 -0
- package/src/formal.ts +179 -0
- package/src/index.ts +6 -3
- package/src/strings.ts +64 -55
- package/src/web.ts +7 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-locale-de",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.260.0",
|
|
4
4
|
"description": "German UI and mail copy for Kumiko framework screens. Opt in by mounting localeDe() + localeDeClient().",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
}
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@cosmicdrift/kumiko-framework": "0.
|
|
31
|
+
"@cosmicdrift/kumiko-framework": "0.260.0"
|
|
32
32
|
},
|
|
33
33
|
"publishConfig": {
|
|
34
34
|
"registry": "https://registry.npmjs.org",
|
|
@@ -663,7 +663,9 @@ export const frameworkEnCatalog: Readonly<Record<string, string>> = {
|
|
|
663
663
|
"screen:api-tokens.title": "Personal Access Tokens",
|
|
664
664
|
"screen:audit-log-detail.title": "Event",
|
|
665
665
|
"screen:audit-log.title": "Audit log",
|
|
666
|
+
"screen:auth-mfa-disable.title": "Disable two-factor authentication",
|
|
666
667
|
"screen:auth-mfa-enable.title": "Two-factor authentication",
|
|
668
|
+
"screen:auth-mfa-regenerate-recovery.title": "Generate new recovery codes",
|
|
667
669
|
"screen:branding-settings.title": "Branding",
|
|
668
670
|
"screen:cap-list.title": "Usage caps",
|
|
669
671
|
"screen:delivery-log.title": "Delivery log",
|
|
@@ -676,6 +678,7 @@ export const frameworkEnCatalog: Readonly<Record<string, string>> = {
|
|
|
676
678
|
"screen:member-roles-edit.title": "Edit roles",
|
|
677
679
|
"screen:members.title": "Team",
|
|
678
680
|
"screen:my-caps.title": "My usage",
|
|
681
|
+
"screen:my-sessions.title": "Your sessions",
|
|
679
682
|
"screen:page-edit.title": "Edit page",
|
|
680
683
|
"screen:page-list.title": "Pages",
|
|
681
684
|
"screen:platform-overview.title": "Overview",
|
|
@@ -710,6 +713,13 @@ export const frameworkEnCatalog: Readonly<Record<string, string>> = {
|
|
|
710
713
|
"sessions.list.col.id": "ID",
|
|
711
714
|
"sessions.list.col.revokedAt": "Revoked",
|
|
712
715
|
"sessions.list.col.userId": "User ID",
|
|
716
|
+
"sessions.mine.col.current": "This device",
|
|
717
|
+
"sessions.mine.col.ip": "IP address",
|
|
718
|
+
"sessions.mine.col.userAgent": "Device",
|
|
719
|
+
"sessions.mine.revoke": "Sign out",
|
|
720
|
+
"sessions.mine.revoke.confirm": "Sign this device out?",
|
|
721
|
+
"sessions.mine.revokeAllOthers": "Sign out all other devices",
|
|
722
|
+
"sessions.mine.revokeAllOthers.confirm": "Sign out every device except this one?",
|
|
713
723
|
"sessions:nav.sessionList": "Sessions",
|
|
714
724
|
"subscription-stripe.api-key": "Stripe API Key",
|
|
715
725
|
"subscription-stripe.billing-live": "Stripe Billing Live",
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { germanBundleFor, localeDeFormalOverrides } from "../formal";
|
|
3
|
+
import { localeDeBundle } from "../strings";
|
|
4
|
+
import { localeDeClient } from "../web";
|
|
5
|
+
|
|
6
|
+
function placeholders(s: string): string[] {
|
|
7
|
+
return [...s.matchAll(/\{(\w+)\}/g)].map((m) => m[1] ?? "").sort();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// \b only matches at an ASCII-word-char boundary, so "du"/"dir"/"dein..."
|
|
11
|
+
// only hits whole words (not substrings like "Modul" or "administrieren");
|
|
12
|
+
// dein\w* also catches inflected forms (deiner, deinem, deines, ...).
|
|
13
|
+
const duFormRegex = /\b(du|dich|dir|dein\w*|Du|Dich|Dir|Dein\w*)\b/;
|
|
14
|
+
|
|
15
|
+
describe("locale-de formal overrides", () => {
|
|
16
|
+
test("every override key exists in localeDeBundle", () => {
|
|
17
|
+
const orphaned = Object.keys(localeDeFormalOverrides).filter(
|
|
18
|
+
(k) => localeDeBundle[k] === undefined,
|
|
19
|
+
);
|
|
20
|
+
expect(orphaned).toEqual([]);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("override placeholders match the original", () => {
|
|
24
|
+
for (const [key, value] of Object.entries(localeDeFormalOverrides)) {
|
|
25
|
+
expect(placeholders(value)).toEqual(placeholders(localeDeBundle[key] ?? ""));
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("formal bundle contains no informal (du) forms", () => {
|
|
30
|
+
const formalBundle = germanBundleFor("formal");
|
|
31
|
+
const offenders = Object.entries(formalBundle)
|
|
32
|
+
.filter(([, value]) => duFormRegex.test(value))
|
|
33
|
+
.map(([key]) => key);
|
|
34
|
+
expect(offenders).toEqual([]);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("informal bundle is unchanged and still uses du forms", () => {
|
|
38
|
+
const informalBundle = germanBundleFor("informal");
|
|
39
|
+
expect(informalBundle).toBe(localeDeBundle);
|
|
40
|
+
expect(informalBundle["auth.resetPassword.intro"]).toBe(
|
|
41
|
+
"Wähle ein neues Passwort mit mindestens 8 Zeichen.",
|
|
42
|
+
);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("localeDeClient defaults to informal and switches to formal on request", () => {
|
|
46
|
+
expect(localeDeClient().translations["de"]["auth.resetPassword.intro"]).toBe(
|
|
47
|
+
"Wähle ein neues Passwort mit mindestens 8 Zeichen.",
|
|
48
|
+
);
|
|
49
|
+
expect(
|
|
50
|
+
localeDeClient({ address: "formal" }).translations["de"]["auth.resetPassword.intro"],
|
|
51
|
+
).toBe("Wählen Sie ein neues Passwort mit mindestens 8 Zeichen.");
|
|
52
|
+
});
|
|
53
|
+
});
|
package/src/formal.ts
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
// @runtime client
|
|
2
|
+
import { localeDeBundle } from "./strings";
|
|
3
|
+
|
|
4
|
+
export type GermanAddress = "informal" | "formal";
|
|
5
|
+
|
|
6
|
+
export const localeDeFormalOverrides: Readonly<Record<string, string>> = {
|
|
7
|
+
"auth.errors.accountRestricted":
|
|
8
|
+
"Konto nach Art. 18 DSGVO eingeschränkt. Heben Sie die Einschränkung auf, um sich wieder anzumelden.",
|
|
9
|
+
"auth.errors.invalidResetToken":
|
|
10
|
+
"Der Link ist ungültig oder abgelaufen. Bitte fordern Sie einen neuen an.",
|
|
11
|
+
"auth.errors.invalidSignupToken":
|
|
12
|
+
"Der Aktivierungs-Link ist ungültig oder abgelaufen. Bitte fordern Sie einen neuen an.",
|
|
13
|
+
"auth.errors.invalidUnlockToken":
|
|
14
|
+
"Der Entsperren-Link ist ungültig oder abgelaufen. Bitte fordern Sie einen neuen an.",
|
|
15
|
+
"auth.errors.signupEmailAlreadyRegistered":
|
|
16
|
+
"Für diese E-Mail-Adresse existiert bereits ein Konto. Bitte loggen Sie sich ein oder setzen Sie Ihr Passwort zurück.",
|
|
17
|
+
"auth.forgotPassword.intro":
|
|
18
|
+
"Geben Sie Ihre E-Mail-Adresse ein. Falls ein Konto existiert, schicken wir Ihnen einen Reset-Link.",
|
|
19
|
+
"auth.forgotPassword.successBody":
|
|
20
|
+
"Falls die E-Mail in unserem System existiert, ist eine Nachricht mit einem Reset-Link unterwegs. Bitte schauen Sie in Ihren Posteingang.",
|
|
21
|
+
"auth.inviteAccept.intro":
|
|
22
|
+
"Sie wurden zu einem Workspace eingeladen. Klicken Sie auf „Annehmen“, um Mitglied zu werden.",
|
|
23
|
+
"auth.inviteAccept.loggedInAs":
|
|
24
|
+
"Sie sind als {email} angemeldet. Klicken Sie auf „Annehmen“, um Mitglied zu werden.",
|
|
25
|
+
"auth.login.resendRateLimited": "Bitte warten Sie kurz und versuchen Sie es erneut.",
|
|
26
|
+
"auth.login.resendSuccess": "Wir haben Ihnen eine neue Bestätigungs-Mail geschickt.",
|
|
27
|
+
"auth.mail.activation.ignore":
|
|
28
|
+
"Falls Sie sich nicht registriert haben, können Sie diese E-Mail ignorieren. Es wird kein Account erstellt, solange Sie den Link nicht öffnen.",
|
|
29
|
+
"auth.mail.activation.intro":
|
|
30
|
+
"klicken Sie auf den folgenden Link, um Ihren {app}-Account zu aktivieren. Im nächsten Schritt setzen Sie Ihr Passwort:",
|
|
31
|
+
"auth.mail.invite.ignore":
|
|
32
|
+
"Falls Sie diese Einladung nicht erwartet haben, können Sie diese E-Mail ignorieren.",
|
|
33
|
+
"auth.mail.invite.intro":
|
|
34
|
+
"Sie wurden zu einem {app}-Workspace als {role} eingeladen. Klicken Sie auf den folgenden Link, um die Einladung anzunehmen:",
|
|
35
|
+
"auth.mail.reset.ignore":
|
|
36
|
+
"Falls Sie keinen Reset angefordert haben, können Sie diese E-Mail einfach ignorieren. Ihr Passwort bleibt unverändert.",
|
|
37
|
+
"auth.mail.reset.intro":
|
|
38
|
+
"Sie haben den Reset Ihres Passworts für {app} angefordert. Klicken Sie auf den folgenden Link, um ein neues Passwort zu setzen:",
|
|
39
|
+
"auth.mail.unlock.ignore":
|
|
40
|
+
"Falls Sie diese Sperre nicht ausgelöst haben, können Sie diese E-Mail ignorieren. Die Sperre läuft von selbst wieder ab.",
|
|
41
|
+
"auth.mail.unlock.intro":
|
|
42
|
+
"Ihr {app}-Konto wurde nach mehreren fehlgeschlagenen Anmeldeversuchen vorübergehend gesperrt. Klicken Sie auf den folgenden Link, um es sofort zu entsperren:",
|
|
43
|
+
"auth.mail.verify.ignore":
|
|
44
|
+
"Falls Sie dieses Konto nicht angelegt haben, können Sie diese E-Mail ignorieren.",
|
|
45
|
+
"auth.mail.verify.intro":
|
|
46
|
+
"bitte bestätigen Sie Ihre E-Mail-Adresse für {app}, um Ihr Konto zu aktivieren:",
|
|
47
|
+
"auth.mfa.disable.description":
|
|
48
|
+
"Bestätigen Sie mit einem Code aus Ihrer Authenticator-App oder einem Recovery-Code. Ihr Konto ist danach nur noch durch Ihr Passwort geschützt.",
|
|
49
|
+
"auth.mfa.enable.intro":
|
|
50
|
+
"Schützen Sie Ihr Konto zusätzlich mit einer Authenticator-App wie Google Authenticator oder 1Password.",
|
|
51
|
+
"auth.mfa.enable.recoveryHint":
|
|
52
|
+
"Speichern Sie diese Codes an einem sicheren Ort. Sie werden nur dieses eine Mal angezeigt und erlauben Ihnen den Zugriff, falls Sie Ihr Gerät verlieren.",
|
|
53
|
+
"auth.mfa.regenerate.description":
|
|
54
|
+
"Bestätigen Sie mit einem Code aus Ihrer Authenticator-App. Alle bisherigen Recovery-Codes werden sofort ungültig.",
|
|
55
|
+
"auth.mfa.regenerate.newCodesHint":
|
|
56
|
+
"Speichern Sie diese Codes an einem sicheren Ort. Die alten Codes funktionieren ab sofort nicht mehr.",
|
|
57
|
+
"auth.mfa.regenerate.newCodesTitle": "Ihre neuen Recovery-Codes",
|
|
58
|
+
"auth.mfa.setup.subtitle":
|
|
59
|
+
"Ihr Konto verlangt Zwei-Faktor-Authentifizierung. Richten Sie sie jetzt ein, um sich anzumelden.",
|
|
60
|
+
"auth.mfa.verify.subtitle": "Geben Sie den 6-stelligen Code aus Ihrer Authenticator-App ein.",
|
|
61
|
+
"auth.requestUnlock.intro":
|
|
62
|
+
"Geben Sie Ihre E-Mail-Adresse ein. Falls Ihr Konto gesperrt ist, schicken wir Ihnen einen Entsperren-Link.",
|
|
63
|
+
"auth.requestUnlock.successBody":
|
|
64
|
+
"Falls die E-Mail in unserem System existiert und gesperrt ist, ist eine Nachricht mit einem Entsperren-Link unterwegs. Bitte schauen Sie in Ihren Posteingang.",
|
|
65
|
+
"auth.resetPassword.intro": "Wählen Sie ein neues Passwort mit mindestens 8 Zeichen.",
|
|
66
|
+
"auth.resetPassword.missingToken":
|
|
67
|
+
"Der Reset-Link enthält keinen Token. Bitte fordern Sie einen neuen an.",
|
|
68
|
+
"auth.resetPassword.successBody": "Sie können sich jetzt mit Ihrem neuen Passwort anmelden.",
|
|
69
|
+
"auth.signup.intro":
|
|
70
|
+
"Geben Sie Ihre E-Mail-Adresse ein. Wir schicken Ihnen einen Aktivierungs-Link, mit dem Sie Ihr Passwort setzen.",
|
|
71
|
+
"auth.signup.successBody":
|
|
72
|
+
"Wir haben Ihnen einen Aktivierungs-Link an Ihre E-Mail-Adresse geschickt. Klicken Sie ihn an, um Ihr Passwort zu setzen und sich einzuloggen.",
|
|
73
|
+
"auth.signupComplete.activated": "Ihr Konto ist jetzt aktiv und Sie sind angemeldet.",
|
|
74
|
+
"auth.signupComplete.intro":
|
|
75
|
+
"Wählen Sie ein Passwort mit mindestens 8 Zeichen für Ihren neuen Account.",
|
|
76
|
+
"auth.signupComplete.missingToken":
|
|
77
|
+
"Der Aktivierungs-Link enthält keinen Token. Bitte fordern Sie einen neuen an.",
|
|
78
|
+
"auth.unlockAccount.errorBody":
|
|
79
|
+
"Der Link ist ungültig oder abgelaufen. Bitte fordern Sie einen neuen Entsperren-Link an.",
|
|
80
|
+
"auth.unlockAccount.successBody":
|
|
81
|
+
"Ihr Konto ist wieder entsperrt. Sie können sich jetzt anmelden.",
|
|
82
|
+
"auth.verifyEmail.errorBody":
|
|
83
|
+
"Der Link ist ungültig oder abgelaufen. Bitte fordern Sie eine neue Bestätigungs-Mail an.",
|
|
84
|
+
"auth.verifyEmail.successBody": "Danke! Sie können sich jetzt anmelden.",
|
|
85
|
+
"custom-fields.form.createMode": "Speichern Sie zuerst den Eintrag, um Custom-Felder zu setzen.",
|
|
86
|
+
"dispatcher.errors.network":
|
|
87
|
+
"Netzwerkfehler. Bitte überprüfen Sie Ihre Verbindung und versuchen Sie es erneut.",
|
|
88
|
+
"errors.access.denied": "Dazu haben Sie keine Berechtigung.",
|
|
89
|
+
"errors.download.urlMissing": "Download nicht verfügbar. Bitte versuchen Sie es erneut.",
|
|
90
|
+
"errors.internal": "Etwas ist schiefgegangen. Bitte versuchen Sie es später erneut.",
|
|
91
|
+
"errors.rate_limited": "Zu viele Anfragen. Bitte versuchen Sie es in Kürze erneut.",
|
|
92
|
+
"folders.section.createMode": "Speichern Sie zuerst den Eintrag, um einen Ordner zu wählen.",
|
|
93
|
+
"gdpr.mail.deletionExecuted.intro":
|
|
94
|
+
"Ihr {app}-Konto und die zugehoerigen personenbezogenen Daten wurden am {when} geloescht. Diese Aktion ist endgueltig.",
|
|
95
|
+
"gdpr.mail.deletionExecuted.subject": "{app} — Ihr Konto wurde geloescht",
|
|
96
|
+
"gdpr.mail.deletionRequested.cancel":
|
|
97
|
+
"Falls Sie das nicht angefordert haben, melden Sie sich an und brechen Sie die Loeschung in den Kontoeinstellungen ab, bevor die Frist ablaeuft.",
|
|
98
|
+
"gdpr.mail.deletionRequested.intro":
|
|
99
|
+
"wir haben Ihren Antrag zur Loeschung Ihres {app}-Kontos erhalten. Ihr Konto und die zugehoerigen Daten werden am {when} endgueltig geloescht.",
|
|
100
|
+
"gdpr.mail.deletionRequested.subject": "{app} — Loeschung Ihres Kontos angefordert",
|
|
101
|
+
"gdpr.mail.exportFailed.intro":
|
|
102
|
+
"Ihr angeforderter Datenexport fuer {app} konnte leider nicht erstellt werden. Bitte fordern Sie den Export erneut an.",
|
|
103
|
+
"gdpr.mail.exportFailed.subject": "{app} — Ihr Datenexport ist fehlgeschlagen",
|
|
104
|
+
"gdpr.mail.exportReady.intro":
|
|
105
|
+
"Ihr angeforderter Datenexport fuer {app} ist fertig. Laden Sie ihn ueber den folgenden Link herunter:",
|
|
106
|
+
"gdpr.mail.exportReady.subject": "{app} — Ihr Datenexport ist bereit",
|
|
107
|
+
"kumiko.field.reference-created-no-id":
|
|
108
|
+
"Der Eintrag wurde angelegt, aber nicht automatisch ausgewählt. Bitte wählen Sie ihn manuell aus.",
|
|
109
|
+
"kumiko.form.draft.resume-multiple":
|
|
110
|
+
"Mehrere offene Entwürfe für dieses Formular gefunden. Welchen möchten Sie fortsetzen?",
|
|
111
|
+
"kumiko.form.draft.resume-single":
|
|
112
|
+
"Ein offener Entwurf für dieses Formular gefunden. Möchten Sie ihn fortsetzen?",
|
|
113
|
+
"notesHistory.section.createMode": "Speichern Sie zuerst den Eintrag, um Notizen anzulegen.",
|
|
114
|
+
"pat.create.needPassword": "Bitte Ihr Passwort zur Bestätigung eingeben.",
|
|
115
|
+
"pat.create.subtitle":
|
|
116
|
+
"Wählen Sie Berechtigungen und eine Gültigkeit. Der Token wird nur einmal angezeigt.",
|
|
117
|
+
"pat.list.title": "Ihre Tokens",
|
|
118
|
+
"screen:my-sessions.title": "Ihre Sitzungen",
|
|
119
|
+
"profile.danger.cancelSuccess": "Löschung abgebrochen. Ihr Konto bleibt bestehen.",
|
|
120
|
+
"profile.danger.dialogDescription":
|
|
121
|
+
"Nach Ablauf der Frist werden Ihre Daten endgültig gelöscht. Bis dahin können Sie die Löschung abbrechen.",
|
|
122
|
+
"profile.danger.explainer":
|
|
123
|
+
"Ihr Konto wird nach einer Frist endgültig gelöscht. Bis dahin können Sie die Löschung jederzeit abbrechen.",
|
|
124
|
+
"profile.danger.requested": "Löschung beantragt. Ihr Konto wird am {date} endgültig gelöscht.",
|
|
125
|
+
"profile.email.success": "E-Mail geändert. Bitte bestätigen Sie Ihre neue Adresse.",
|
|
126
|
+
"profile.errors.emailUnchanged": "Das ist bereits Ihre E-Mail-Adresse.",
|
|
127
|
+
"tags.section.createMode": "Speichern Sie zuerst den Eintrag, um Tags zu setzen.",
|
|
128
|
+
"userDataRights.deletion.confirm.intro":
|
|
129
|
+
"Mit dem Bestätigen startet die Lösch-Frist. Bis sie abläuft können Sie die Löschung im eingeloggten Account wieder abbrechen.",
|
|
130
|
+
"userDataRights.deletion.confirm.invalidToken":
|
|
131
|
+
"Der Link ist ungültig oder abgelaufen. Bitte fordern Sie einen neuen an.",
|
|
132
|
+
"userDataRights.deletion.confirm.missingToken":
|
|
133
|
+
"Kein Token im Link gefunden. Bitte öffnen Sie den Link aus der E-Mail erneut.",
|
|
134
|
+
"userDataRights.deletion.confirm.successBody":
|
|
135
|
+
"Ihr Account wird nach Ablauf der Frist gelöscht. Sie können die Löschung bis dahin im eingeloggten Account abbrechen.",
|
|
136
|
+
"userDataRights.deletion.request.intro":
|
|
137
|
+
"Geben Sie die E-Mail-Adresse Ihres Kontos ein. Falls ein Konto existiert, schicken wir Ihnen einen Bestätigungs-Link zum Löschen.",
|
|
138
|
+
"userDataRights.deletion.request.successBody":
|
|
139
|
+
"Falls die E-Mail in unserem System existiert, ist eine Nachricht mit einem Bestätigungs-Link unterwegs. Bitte schauen Sie in Ihren Posteingang.",
|
|
140
|
+
"userDataRights.errors.download.expired":
|
|
141
|
+
"Ihr Download ist abgelaufen. Bitte fordern Sie einen neuen Export an.",
|
|
142
|
+
"userDataRights.errors.download.signedUrlNotSupported":
|
|
143
|
+
"Der Download ist derzeit nicht möglich. Wir wurden bereits informiert, bitte versuchen Sie es später erneut.",
|
|
144
|
+
"userDataRights.errors.download.unavailable":
|
|
145
|
+
"Der Export ist noch nicht fertig oder fehlgeschlagen. Bitte prüfen Sie den Status.",
|
|
146
|
+
"userDataRights.privacyCenter.deletion.dialogDescription":
|
|
147
|
+
"Mit dem Bestätigen startet die Lösch-Frist. Sie können die Löschung bis zu ihrem Ablauf wieder abbrechen.",
|
|
148
|
+
"userDataRights.privacyCenter.deletion.explainer":
|
|
149
|
+
"Beantragen Sie die Löschung Ihres Kontos. Bis zum Ablauf der Frist können Sie die Löschung wieder abbrechen.",
|
|
150
|
+
"userDataRights.privacyCenter.deletion.requested": "Ihr Konto wird am {date} gelöscht.",
|
|
151
|
+
"userDataRights.privacyCenter.export.failed":
|
|
152
|
+
"Die letzte Export-Erstellung ist fehlgeschlagen. Sie können es erneut versuchen.",
|
|
153
|
+
"userDataRights.privacyCenter.export.intro":
|
|
154
|
+
"Fordern Sie eine Kopie Ihrer Daten an. Die Erstellung läuft im Hintergrund; sobald sie fertig ist, können Sie sie hier herunterladen.",
|
|
155
|
+
"userDataRights.privacyCenter.export.pending":
|
|
156
|
+
"Ihr Export wird erstellt. Bitte später erneut schauen.",
|
|
157
|
+
"userDataRights.privacyCenter.export.ready": "Ihr Export ist fertig.",
|
|
158
|
+
"userDataRights.privacyCenter.intro":
|
|
159
|
+
"Verwalten Sie Ihre Rechte nach DSGVO: Datenauskunft, Export, Einschränkung und Löschung Ihres Kontos.",
|
|
160
|
+
"userDataRights.privacyCenter.loadError": "Ihre Daten konnten nicht geladen werden.",
|
|
161
|
+
"userDataRights.privacyCenter.restriction.dialogDescription":
|
|
162
|
+
"Sie werden sofort abgemeldet und können sich nicht mehr anmelden, bis der Support die Einschränkung aufhebt.",
|
|
163
|
+
"userDataRights.privacyCenter.restriction.explainer":
|
|
164
|
+
"Die Verarbeitung Ihrer Daten wird pausiert, und Sie werden sofort abgemeldet. Nur der Support kann die Einschränkung danach wieder aufheben.",
|
|
165
|
+
"userDataRights.privacyCenter.restriction.restricted":
|
|
166
|
+
"Ihr Konto ist eingeschränkt. Wenden Sie sich an den Support, um die Einschränkung aufzuheben.",
|
|
167
|
+
"pat.created.hint":
|
|
168
|
+
"Kopieren Sie diesen Token jetzt. Aus Sicherheitsgründen wird er nur dieses eine Mal angezeigt.",
|
|
169
|
+
"kumiko.list.empty.hint": "Legen Sie den ersten an, um loszulegen.",
|
|
170
|
+
"auth.mfa.setup.intro":
|
|
171
|
+
"Scannen Sie den QR-Code mit einer Authenticator-App wie Google Authenticator oder 1Password.",
|
|
172
|
+
"tier-admin.explainer":
|
|
173
|
+
"Weisen Sie einem Mandanten ein Tier ohne Kauf zu. Die manuelle Zuweisung bleibt bestehen, auch wenn sich die Abrechnung später automatisch aktualisiert.",
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
export function germanBundleFor(address: GermanAddress): Readonly<Record<string, string>> {
|
|
177
|
+
if (address === "informal") return localeDeBundle;
|
|
178
|
+
return { ...localeDeBundle, ...localeDeFormalOverrides };
|
|
179
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
import { defineFeature } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
2
|
import { registerMailTranslations } from "@cosmicdrift/kumiko-framework/i18n";
|
|
3
|
+
import { type GermanAddress, germanBundleFor } from "./formal";
|
|
3
4
|
import { localeDeBundle } from "./strings";
|
|
4
5
|
|
|
6
|
+
export type { GermanAddress };
|
|
5
7
|
export { localeDeBundle };
|
|
6
8
|
|
|
7
|
-
export function localeDe() {
|
|
8
|
-
|
|
9
|
+
export function localeDe(options?: { readonly address?: GermanAddress }) {
|
|
10
|
+
const bundle = germanBundleFor(options?.address ?? "informal");
|
|
11
|
+
registerMailTranslations("de", bundle);
|
|
9
12
|
return defineFeature("locale-de", (r) => {
|
|
10
13
|
r.describe(
|
|
11
14
|
"German UI and mail copy for framework screens. Opt-in; not included by includeBundled.",
|
|
12
15
|
);
|
|
13
16
|
r.translations({
|
|
14
|
-
keys: Object.fromEntries(Object.entries(
|
|
17
|
+
keys: Object.fromEntries(Object.entries(bundle).map(([k, v]) => [k, { de: v }])),
|
|
15
18
|
});
|
|
16
19
|
});
|
|
17
20
|
}
|
package/src/strings.ts
CHANGED
|
@@ -45,12 +45,12 @@ export const localeDeBundle: Readonly<Record<string, string>> = {
|
|
|
45
45
|
"audit.log.loading": "Lade Ereignisse…",
|
|
46
46
|
"audit.log.newest": "Neueste",
|
|
47
47
|
"audit.log.older": "Ältere laden",
|
|
48
|
-
"audit.log.title": "
|
|
48
|
+
"audit.log.title": "Ereignisprotokoll",
|
|
49
49
|
"audit:nav.auditLog": "Audit",
|
|
50
50
|
"auth.errors.accountLocked": "Konto vorübergehend gesperrt.",
|
|
51
51
|
"auth.errors.accountLockedRetry": "Konto gesperrt. Neuer Versuch in {minutes} Minuten.",
|
|
52
52
|
"auth.errors.accountRestricted":
|
|
53
|
-
"Konto
|
|
53
|
+
"Konto nach Art. 18 DSGVO eingeschränkt. Hebe die Einschränkung auf, um dich wieder anzumelden.",
|
|
54
54
|
"auth.errors.emailNotVerified": "E-Mail-Adresse noch nicht bestätigt.",
|
|
55
55
|
"auth.errors.invalidBody": "Ungültige Eingabe.",
|
|
56
56
|
"auth.errors.invalidCredentials": "E-Mail oder Passwort falsch.",
|
|
@@ -86,9 +86,9 @@ export const localeDeBundle: Readonly<Record<string, string>> = {
|
|
|
86
86
|
"auth.inviteAccept.email": "E-Mail",
|
|
87
87
|
"auth.inviteAccept.goToLogin": "Zum Login",
|
|
88
88
|
"auth.inviteAccept.intro":
|
|
89
|
-
"Du wurdest zu einem Workspace eingeladen. Klicke auf
|
|
89
|
+
"Du wurdest zu einem Workspace eingeladen. Klicke auf „Annehmen“, um Mitglied zu werden.",
|
|
90
90
|
"auth.inviteAccept.loggedInAs":
|
|
91
|
-
"Du bist als {email}
|
|
91
|
+
"Du bist als {email} angemeldet. Klicke auf „Annehmen“, um Mitglied zu werden.",
|
|
92
92
|
"auth.inviteAccept.missingToken": "Der Einladungs-Link enthält keinen Token oder ist ungültig.",
|
|
93
93
|
"auth.inviteAccept.password": "Passwort",
|
|
94
94
|
"auth.inviteAccept.submit": "Annehmen + Anmelden",
|
|
@@ -112,7 +112,7 @@ export const localeDeBundle: Readonly<Record<string, string>> = {
|
|
|
112
112
|
"auth.mail.activation.expiry": "Der Link läuft am {when} ab.",
|
|
113
113
|
"auth.mail.activation.greeting": "Willkommen,",
|
|
114
114
|
"auth.mail.activation.ignore":
|
|
115
|
-
"Falls du dich nicht registriert hast, kannst du diese E-Mail ignorieren
|
|
115
|
+
"Falls du dich nicht registriert hast, kannst du diese E-Mail ignorieren. Es wird kein Account erstellt, solange du den Link nicht öffnest.",
|
|
116
116
|
"auth.mail.activation.intro":
|
|
117
117
|
"klicke auf den folgenden Link, um deinen {app}-Account zu aktivieren. Im nächsten Schritt setzt du dein Passwort:",
|
|
118
118
|
"auth.mail.activation.subject": "{app} — Account aktivieren",
|
|
@@ -129,7 +129,7 @@ export const localeDeBundle: Readonly<Record<string, string>> = {
|
|
|
129
129
|
"auth.mail.reset.expiry": "Der Link läuft am {when} ab.",
|
|
130
130
|
"auth.mail.reset.greeting": "Hallo,",
|
|
131
131
|
"auth.mail.reset.ignore":
|
|
132
|
-
"Falls du keinen Reset angefordert hast, kannst du diese E-Mail einfach ignorieren
|
|
132
|
+
"Falls du keinen Reset angefordert hast, kannst du diese E-Mail einfach ignorieren. Dein Passwort bleibt unverändert.",
|
|
133
133
|
"auth.mail.reset.intro":
|
|
134
134
|
"du hast den Reset deines Passworts für {app} angefordert. Klicke auf den folgenden Link, um ein neues Passwort zu setzen:",
|
|
135
135
|
"auth.mail.reset.subject": "{app} — Passwort zurücksetzen",
|
|
@@ -137,7 +137,7 @@ export const localeDeBundle: Readonly<Record<string, string>> = {
|
|
|
137
137
|
"auth.mail.unlock.expiry": "Der Link läuft am {when} ab.",
|
|
138
138
|
"auth.mail.unlock.greeting": "Hallo,",
|
|
139
139
|
"auth.mail.unlock.ignore":
|
|
140
|
-
"Falls du diese Sperre nicht ausgelöst hast, kannst du diese E-Mail ignorieren
|
|
140
|
+
"Falls du diese Sperre nicht ausgelöst hast, kannst du diese E-Mail ignorieren. Die Sperre läuft von selbst wieder ab.",
|
|
141
141
|
"auth.mail.unlock.intro":
|
|
142
142
|
"dein {app}-Konto wurde nach mehreren fehlgeschlagenen Anmeldeversuchen vorübergehend gesperrt. Klicke auf den folgenden Link, um es sofort zu entsperren:",
|
|
143
143
|
"auth.mail.unlock.subject": "{app} — Konto entsperren",
|
|
@@ -161,7 +161,7 @@ export const localeDeBundle: Readonly<Record<string, string>> = {
|
|
|
161
161
|
"auth.mfa.enable.code": "Code aus der Authenticator-App",
|
|
162
162
|
"auth.mfa.enable.confirm": "Aktivieren",
|
|
163
163
|
"auth.mfa.enable.intro":
|
|
164
|
-
"Schütze dein Konto zusätzlich mit einer Authenticator-App
|
|
164
|
+
"Schütze dein Konto zusätzlich mit einer Authenticator-App wie Google Authenticator oder 1Password.",
|
|
165
165
|
"auth.mfa.enable.manualEntry": "Oder manuell eingeben:",
|
|
166
166
|
"auth.mfa.enable.recoveryHint":
|
|
167
167
|
"Speichere diese Codes an einem sicheren Ort. Sie werden nur dieses eine Mal angezeigt und erlauben dir den Zugriff, falls du dein Gerät verlierst.",
|
|
@@ -197,7 +197,7 @@ export const localeDeBundle: Readonly<Record<string, string>> = {
|
|
|
197
197
|
"auth.mfa.regenerate.trigger": "Neue Recovery-Codes erzeugen",
|
|
198
198
|
"auth.mfa.setup.confirm": "Einrichtung abschließen",
|
|
199
199
|
"auth.mfa.setup.intro":
|
|
200
|
-
"Scanne den QR-Code mit einer Authenticator-App
|
|
200
|
+
"Scanne den QR-Code mit einer Authenticator-App wie Google Authenticator oder 1Password.",
|
|
201
201
|
"auth.mfa.setup.start": "Einrichtung starten",
|
|
202
202
|
"auth.mfa.setup.subtitle":
|
|
203
203
|
"Dein Konto verlangt Zwei-Faktor-Authentifizierung. Richte sie jetzt ein, um dich anzumelden.",
|
|
@@ -221,7 +221,7 @@ export const localeDeBundle: Readonly<Record<string, string>> = {
|
|
|
221
221
|
"auth.requestUnlock.title": "Konto entsperren",
|
|
222
222
|
"auth.resetPassword.confirmPassword": "Passwort bestätigen",
|
|
223
223
|
"auth.resetPassword.goToLogin": "Zum Login",
|
|
224
|
-
"auth.resetPassword.intro": "Wähle ein neues Passwort
|
|
224
|
+
"auth.resetPassword.intro": "Wähle ein neues Passwort mit mindestens 8 Zeichen.",
|
|
225
225
|
"auth.resetPassword.mismatch": "Die Passwörter stimmen nicht überein.",
|
|
226
226
|
"auth.resetPassword.missingToken":
|
|
227
227
|
"Der Reset-Link enthält keinen Token. Bitte fordere einen neuen an.",
|
|
@@ -248,7 +248,7 @@ export const localeDeBundle: Readonly<Record<string, string>> = {
|
|
|
248
248
|
"auth.signupComplete.confirmPassword": "Passwort bestätigen",
|
|
249
249
|
"auth.signupComplete.continue": "Weiter",
|
|
250
250
|
"auth.signupComplete.intro":
|
|
251
|
-
"Wähle ein Passwort
|
|
251
|
+
"Wähle ein Passwort mit mindestens 8 Zeichen für deinen neuen Account.",
|
|
252
252
|
"auth.signupComplete.mismatch": "Die Passwörter stimmen nicht überein.",
|
|
253
253
|
"auth.signupComplete.missingToken":
|
|
254
254
|
"Der Aktivierungs-Link enthält keinen Token. Bitte fordere einen neuen an.",
|
|
@@ -284,8 +284,7 @@ export const localeDeBundle: Readonly<Record<string, string>> = {
|
|
|
284
284
|
"cap-counter:entity:cap-counter:field:value": "Wert",
|
|
285
285
|
"cap-overview.cards.empty": "Für diesen Mandanten sind keine Caps konfiguriert.",
|
|
286
286
|
"cap-overview.cards.loading": "Lade Nutzungsdaten …",
|
|
287
|
-
"cap-overview.errors.progressPrimitiveMissing":
|
|
288
|
-
"Nutzungsbalken nicht verfügbar — Progress-Primitive ist nicht registriert.",
|
|
287
|
+
"cap-overview.errors.progressPrimitiveMissing": "Nutzungsbalken nicht verfügbar.",
|
|
289
288
|
"cap-overview.errors.sortFieldUnsupported": "Sortierung nach diesem Feld wird nicht unterstützt.",
|
|
290
289
|
"cap-overview.errors.tenantOverrideRequiresSystemAdmin":
|
|
291
290
|
"Nur SystemAdmin darf Daten eines anderen Mandanten abfragen.",
|
|
@@ -307,7 +306,7 @@ export const localeDeBundle: Readonly<Record<string, string>> = {
|
|
|
307
306
|
"compliance.profile.select": "Profil",
|
|
308
307
|
"compliance.profile.title": "Compliance-Profil",
|
|
309
308
|
"config.errors.invalidScope": "Diese Ebene ist für diesen Schlüssel nicht zulässig.",
|
|
310
|
-
"config.errors.systemOnly": "Dieser Wert
|
|
309
|
+
"config.errors.systemOnly": "Dieser Wert lässt sich hier nicht ändern.",
|
|
311
310
|
"config.errors.unknownKey": "Unbekannter Konfigurationsschlüssel.",
|
|
312
311
|
"config.secrets.delete": "Löschen",
|
|
313
312
|
"config.secrets.notSet": "Nicht gesetzt",
|
|
@@ -333,7 +332,7 @@ export const localeDeBundle: Readonly<Record<string, string>> = {
|
|
|
333
332
|
"delivery.log.col.type": "Typ",
|
|
334
333
|
"delivery.log.empty": "Keine Zustellversuche.",
|
|
335
334
|
"delivery.log.loading": "Lade Zustellversuche…",
|
|
336
|
-
"delivery.log.title": "
|
|
335
|
+
"delivery.log.title": "Zustellprotokoll",
|
|
337
336
|
"delivery:nav.deliveryLog": "Zustellungen",
|
|
338
337
|
"dispatcher.errors.aborted": "Anfrage abgebrochen.",
|
|
339
338
|
"dispatcher.errors.network":
|
|
@@ -341,8 +340,8 @@ export const localeDeBundle: Readonly<Record<string, string>> = {
|
|
|
341
340
|
"errors.access.denied": "Dazu hast du keine Berechtigung.",
|
|
342
341
|
"errors.cap.exceeded":
|
|
343
342
|
"Limit erreicht. Bitte Tarif upgraden oder auf die nächste Periode warten.",
|
|
344
|
-
"errors.conflict": "
|
|
345
|
-
"errors.download.urlMissing": "Download nicht verfügbar
|
|
343
|
+
"errors.conflict": "Der Vorgang konnte wegen eines Konflikts nicht abgeschlossen werden.",
|
|
344
|
+
"errors.download.urlMissing": "Download nicht verfügbar. Bitte versuche es erneut.",
|
|
346
345
|
"errors.feature.disabled": "Diese Funktion ist derzeit nicht verfügbar.",
|
|
347
346
|
"errors.internal": "Etwas ist schiefgegangen. Bitte versuche es später erneut.",
|
|
348
347
|
"errors.notFound": "Nicht gefunden.",
|
|
@@ -361,12 +360,12 @@ export const localeDeBundle: Readonly<Record<string, string>> = {
|
|
|
361
360
|
"errors.validation.invalid_value": "Ungültige Auswahl.",
|
|
362
361
|
"errors.validation.not_multiple_of": "Muss ein Vielfaches von {divisor} sein.",
|
|
363
362
|
"errors.validation.out_of_bounds": "Wert außerhalb des erlaubten Bereichs.",
|
|
364
|
-
"errors.validation.too_big": "Zu groß oder zu lang
|
|
365
|
-
"errors.validation.too_small": "Zu klein oder zu kurz
|
|
363
|
+
"errors.validation.too_big": "Zu groß oder zu lang. Maximal erlaubt: {maximum}.",
|
|
364
|
+
"errors.validation.too_small": "Zu klein oder zu kurz. Mindestens erforderlich: {minimum}.",
|
|
366
365
|
"errors.validation.unexpected_field": "Unbekanntes Feld.",
|
|
367
366
|
"errors.validation.unrecognized_keys": "Unbekannte Felder.",
|
|
368
367
|
"errors.versionConflict":
|
|
369
|
-
"Der
|
|
368
|
+
"Der Eintrag hat sich inzwischen geändert. Bitte neu laden und erneut versuchen.",
|
|
370
369
|
"feature-toggles.admin.alwaysOn": "immer an",
|
|
371
370
|
"feature-toggles.admin.col.default": "Default",
|
|
372
371
|
"feature-toggles.admin.col.effective": "Effektiv",
|
|
@@ -392,7 +391,7 @@ export const localeDeBundle: Readonly<Record<string, string>> = {
|
|
|
392
391
|
"folders.manager.working": "Speichert…",
|
|
393
392
|
"folders.section.create": "Ordner anlegen & ablegen",
|
|
394
393
|
"folders.section.createMode": "Speichere zuerst den Eintrag, um einen Ordner zu wählen.",
|
|
395
|
-
"folders.section.empty": "
|
|
394
|
+
"folders.section.empty": "Noch keine Ordner angelegt.",
|
|
396
395
|
"folders.section.label": "Ordner",
|
|
397
396
|
"folders.section.loading": "Lädt…",
|
|
398
397
|
"folders.section.newLabel": "Neuer Ordner",
|
|
@@ -419,7 +418,7 @@ export const localeDeBundle: Readonly<Record<string, string>> = {
|
|
|
419
418
|
"gdpr.mail.fallbackUrl":
|
|
420
419
|
"Falls der Button nicht funktioniert, kopiere diesen Link in den Browser:",
|
|
421
420
|
"gdpr.mail.greeting": "Hallo,",
|
|
422
|
-
"jobs.detail.field.duration": "Dauer
|
|
421
|
+
"jobs.detail.field.duration": "Dauer in ms",
|
|
423
422
|
"jobs.detail.field.error": "Fehler",
|
|
424
423
|
"jobs.detail.field.finished": "Beendet",
|
|
425
424
|
"jobs.detail.field.id": "Run-ID",
|
|
@@ -427,8 +426,8 @@ export const localeDeBundle: Readonly<Record<string, string>> = {
|
|
|
427
426
|
"jobs.detail.field.started": "Gestartet",
|
|
428
427
|
"jobs.detail.field.status": "Status",
|
|
429
428
|
"jobs.detail.loading": "Lade Details…",
|
|
430
|
-
"jobs.detail.logs": "
|
|
431
|
-
"jobs.detail.logs.empty": "Keine
|
|
429
|
+
"jobs.detail.logs": "Protokoll",
|
|
430
|
+
"jobs.detail.logs.empty": "Keine Protokollzeilen.",
|
|
432
431
|
"jobs.detail.missing": "Lauf nicht gefunden.",
|
|
433
432
|
"jobs.detail.retry": "Erneut ausführen",
|
|
434
433
|
"jobs.detail.retrying": "Wird erneut gestartet…",
|
|
@@ -436,11 +435,11 @@ export const localeDeBundle: Readonly<Record<string, string>> = {
|
|
|
436
435
|
"jobs.errors.notManual": "Dieser Job ist nicht manuell startbar.",
|
|
437
436
|
"jobs.errors.onlyFailedCanRetry": "Nur fehlgeschlagene Läufe können erneut gestartet werden.",
|
|
438
437
|
"jobs.errors.unknownJob": "Unbekannter Job.",
|
|
439
|
-
"jobs.runs.col.duration": "Dauer
|
|
438
|
+
"jobs.runs.col.duration": "Dauer in ms",
|
|
440
439
|
"jobs.runs.col.job": "Job",
|
|
441
440
|
"jobs.runs.col.started": "Gestartet",
|
|
442
441
|
"jobs.runs.col.status": "Status",
|
|
443
|
-
"jobs.runs.empty": "
|
|
442
|
+
"jobs.runs.empty": "Noch keine Job-Läufe.",
|
|
444
443
|
"jobs.runs.filter.all": "Alle",
|
|
445
444
|
"jobs.runs.filter.completed": "Abgeschlossen",
|
|
446
445
|
"jobs.runs.filter.failed": "Fehlgeschlagen",
|
|
@@ -450,11 +449,11 @@ export const localeDeBundle: Readonly<Record<string, string>> = {
|
|
|
450
449
|
"jobs.runs.loading": "Lade Läufe…",
|
|
451
450
|
"jobs.runs.open": "Details",
|
|
452
451
|
"jobs.runs.title": "Job-Läufe",
|
|
453
|
-
"jobs.trigger.empty": "
|
|
452
|
+
"jobs.trigger.empty": "Es gibt keine Jobs, die sich manuell starten lassen.",
|
|
454
453
|
"jobs.trigger.explainer":
|
|
455
454
|
"Nur manuell startbare Jobs. Nach dem Start erscheint der Lauf in der Historie unten.",
|
|
456
455
|
"jobs.trigger.job": "Job",
|
|
457
|
-
"jobs.trigger.payload": "Payload
|
|
456
|
+
"jobs.trigger.payload": "JSON-Payload",
|
|
458
457
|
"jobs.trigger.payload.hint": "Schema-Hinweis",
|
|
459
458
|
"jobs.trigger.payload.invalidJson": "Payload ist kein gültiges JSON-Objekt.",
|
|
460
459
|
"jobs.trigger.perTenant": "Läuft einmal pro aktivem Mandanten.",
|
|
@@ -494,7 +493,7 @@ export const localeDeBundle: Readonly<Record<string, string>> = {
|
|
|
494
493
|
"kumiko.combobox.search-placeholder": "Suchen…",
|
|
495
494
|
"kumiko.config.cascade.activeMarker": "aktiv",
|
|
496
495
|
"kumiko.config.cascade.noValue": "Kein Wert gesetzt",
|
|
497
|
-
"kumiko.config.cascade.resetTo": "Überschreibung zurücksetzen
|
|
496
|
+
"kumiko.config.cascade.resetTo": "Überschreibung zurücksetzen · {scope}",
|
|
498
497
|
"kumiko.config.source.appOverride": "App-Override",
|
|
499
498
|
"kumiko.config.source.computed": "Berechnet",
|
|
500
499
|
"kumiko.config.source.default": "Standard",
|
|
@@ -525,16 +524,16 @@ export const localeDeBundle: Readonly<Record<string, string>> = {
|
|
|
525
524
|
"kumiko.field.embedded-list.move-down": "Nach unten verschieben",
|
|
526
525
|
"kumiko.field.embedded-list.move-up": "Nach oben verschieben",
|
|
527
526
|
"kumiko.field.embedded-list.paste-cells-unmatched":
|
|
528
|
-
"{count}
|
|
527
|
+
"{count} Zellen ohne passende Option wurden nicht geändert.",
|
|
529
528
|
"kumiko.field.embedded-list.paste-rows-truncated":
|
|
530
|
-
"{count} eingefügte
|
|
529
|
+
"{count} eingefügte Zeilen wurden verworfen, weil die maximale Zeilenzahl erreicht ist.",
|
|
531
530
|
"kumiko.field.embedded-list.remove-row": "Zeile entfernen",
|
|
532
531
|
"kumiko.field.locatedTzHint": "Zeit lokal am angegebenen Ort",
|
|
533
532
|
"kumiko.field.multiSelect.deselect-all": "Alle abwählen",
|
|
534
533
|
"kumiko.field.multiSelect.select-all": "Alle auswählen",
|
|
535
534
|
"kumiko.field.open-calendar": "Kalender öffnen",
|
|
536
535
|
"kumiko.field.reference-created-no-id":
|
|
537
|
-
"
|
|
536
|
+
"Der Eintrag wurde angelegt, aber nicht automatisch ausgewählt. Bitte wähle ihn manuell aus.",
|
|
538
537
|
"kumiko.field.time": "Uhrzeit",
|
|
539
538
|
"kumiko.field.timezone": "Zeitzone",
|
|
540
539
|
"kumiko.field.unsupported": "Dieser Feldtyp kann hier noch nicht bearbeitet werden.",
|
|
@@ -545,14 +544,14 @@ export const localeDeBundle: Readonly<Record<string, string>> = {
|
|
|
545
544
|
"kumiko.form.draft.start-new": "Neu beginnen",
|
|
546
545
|
"kumiko.form.error.generic": "Etwas ist schiefgegangen.",
|
|
547
546
|
"kumiko.form.error.version-conflict":
|
|
548
|
-
"
|
|
547
|
+
"Der Eintrag hat sich inzwischen geändert. Bitte neu laden und erneut versuchen.",
|
|
549
548
|
"kumiko.form.extension.save-failed": "Ein Zusatzfeld konnte nicht gespeichert werden.",
|
|
550
549
|
"kumiko.list.empty.hint": "Lege den ersten an, um loszulegen.",
|
|
551
550
|
"kumiko.list.empty.title": "Noch keine Einträge.",
|
|
552
551
|
"kumiko.list.end-of-list": "— Ende der Liste —",
|
|
553
|
-
"kumiko.list.no-entries": "
|
|
552
|
+
"kumiko.list.no-entries": "Noch keine Einträge.",
|
|
554
553
|
"kumiko.list.related-list-truncated":
|
|
555
|
-
"Zeigt die ersten {count} Einträge. Es gibt weitere, die nicht
|
|
554
|
+
"Zeigt die ersten {count} Einträge. Es gibt weitere, die hier nicht angezeigt werden.",
|
|
556
555
|
"kumiko.list.search-placeholder": "Suchen…",
|
|
557
556
|
"kumiko.list.sort.label": "Sortieren",
|
|
558
557
|
"kumiko.list.sort.unsorted": "Unsortiert",
|
|
@@ -570,8 +569,8 @@ export const localeDeBundle: Readonly<Record<string, string>> = {
|
|
|
570
569
|
"kumiko.validation.invalid": "Ungültiger Wert.",
|
|
571
570
|
"kumiko.validation.out-of-range": "Wert außerhalb des erlaubten Bereichs.",
|
|
572
571
|
"kumiko.validation.required": "Pflichtfeld.",
|
|
573
|
-
"kumiko.validation.too-long": "Zu lang
|
|
574
|
-
"kumiko.validation.too-short": "Zu kurz
|
|
572
|
+
"kumiko.validation.too-long": "Zu lang. Höchstens {max} Zeichen erlaubt.",
|
|
573
|
+
"kumiko.validation.too-short": "Zu kurz. Mindestens {min} Zeichen erforderlich.",
|
|
575
574
|
"kumiko.version.update-available": "Eine neue Version ist verfügbar.",
|
|
576
575
|
"kumiko.widget.drawer.maximize": "Drawer maximieren",
|
|
577
576
|
"kumiko.widget.drawer.resize": "Drawer-Größe ändern",
|
|
@@ -617,9 +616,9 @@ export const localeDeBundle: Readonly<Record<string, string>> = {
|
|
|
617
616
|
"notesHistory.section.loading": "Lädt…",
|
|
618
617
|
"notesHistory.section.meta": "{author} · {date}",
|
|
619
618
|
"notesHistory.section.working": "Speichert…",
|
|
620
|
-
"pat.create.currentPassword": "Passwort
|
|
619
|
+
"pat.create.currentPassword": "Passwort zur Bestätigung",
|
|
621
620
|
"pat.create.expiry": "Gültig bis",
|
|
622
|
-
"pat.create.mfaCode": "2FA-Code
|
|
621
|
+
"pat.create.mfaCode": "2FA-Code, falls aktiviert",
|
|
623
622
|
"pat.create.mfaCodePlaceholder": "6-stelliger Code",
|
|
624
623
|
"pat.create.name": "Name",
|
|
625
624
|
"pat.create.namePlaceholder": "z. B. CLI-Automatisierung",
|
|
@@ -635,7 +634,7 @@ export const localeDeBundle: Readonly<Record<string, string>> = {
|
|
|
635
634
|
"pat.created.copy": "Kopieren",
|
|
636
635
|
"pat.created.dismiss": "Fertig",
|
|
637
636
|
"pat.created.hint":
|
|
638
|
-
"Kopiere diesen Token jetzt
|
|
637
|
+
"Kopiere diesen Token jetzt. Aus Sicherheitsgründen wird er nur dieses eine Mal angezeigt.",
|
|
639
638
|
"pat.error.generic": "Aktion fehlgeschlagen.",
|
|
640
639
|
"pat.expiry.1y": "1 Jahr",
|
|
641
640
|
"pat.expiry.30d": "30 Tage",
|
|
@@ -660,7 +659,7 @@ export const localeDeBundle: Readonly<Record<string, string>> = {
|
|
|
660
659
|
"profile.danger.dialogTitle": "Konto wirklich löschen?",
|
|
661
660
|
"profile.danger.explainer":
|
|
662
661
|
"Dein Konto wird nach einer Frist endgültig gelöscht. Bis dahin kannst du die Löschung jederzeit abbrechen.",
|
|
663
|
-
"profile.danger.requested": "Löschung beantragt
|
|
662
|
+
"profile.danger.requested": "Löschung beantragt. Dein Konto wird am {date} endgültig gelöscht.",
|
|
664
663
|
"profile.danger.title": "Konto löschen",
|
|
665
664
|
"profile.email.current": "Aktuelle E-Mail",
|
|
666
665
|
"profile.email.currentPassword": "Aktuelles Passwort",
|
|
@@ -681,11 +680,13 @@ export const localeDeBundle: Readonly<Record<string, string>> = {
|
|
|
681
680
|
"profile.title": "Profil",
|
|
682
681
|
"screen:api-tokens.title": "Personal Access Tokens",
|
|
683
682
|
"screen:audit-log-detail.title": "Ereignis",
|
|
684
|
-
"screen:audit-log.title": "
|
|
683
|
+
"screen:audit-log.title": "Ereignisprotokoll",
|
|
684
|
+
"screen:auth-mfa-disable.title": "Zwei-Faktor-Authentifizierung deaktivieren",
|
|
685
685
|
"screen:auth-mfa-enable.title": "Zwei-Faktor-Authentifizierung",
|
|
686
|
+
"screen:auth-mfa-regenerate-recovery.title": "Neue Recovery-Codes erzeugen",
|
|
686
687
|
"screen:branding-settings.title": "Branding",
|
|
687
688
|
"screen:cap-list.title": "Nutzungslimits",
|
|
688
|
-
"screen:delivery-log.title": "
|
|
689
|
+
"screen:delivery-log.title": "Zustellprotokoll",
|
|
689
690
|
"screen:download-attempt-list.title": "Download-Versuche",
|
|
690
691
|
"screen:export-job-detail.title": "Export-Job",
|
|
691
692
|
"screen:export-job-list.title": "DSGVO-Exporte",
|
|
@@ -695,6 +696,7 @@ export const localeDeBundle: Readonly<Record<string, string>> = {
|
|
|
695
696
|
"screen:member-roles-edit.title": "Rollen bearbeiten",
|
|
696
697
|
"screen:members.title": "Team",
|
|
697
698
|
"screen:my-caps.title": "Meine Nutzung",
|
|
699
|
+
"screen:my-sessions.title": "Deine Sitzungen",
|
|
698
700
|
"screen:page-edit.title": "Seite bearbeiten",
|
|
699
701
|
"screen:page-list.title": "Seiten",
|
|
700
702
|
"screen:platform-overview.title": "Übersicht",
|
|
@@ -729,6 +731,13 @@ export const localeDeBundle: Readonly<Record<string, string>> = {
|
|
|
729
731
|
"sessions.list.col.id": "ID",
|
|
730
732
|
"sessions.list.col.revokedAt": "Widerrufen",
|
|
731
733
|
"sessions.list.col.userId": "User-ID",
|
|
734
|
+
"sessions.mine.col.current": "Dieses Gerät",
|
|
735
|
+
"sessions.mine.col.ip": "IP-Adresse",
|
|
736
|
+
"sessions.mine.col.userAgent": "Gerät",
|
|
737
|
+
"sessions.mine.revoke": "Abmelden",
|
|
738
|
+
"sessions.mine.revoke.confirm": "Dieses Gerät abmelden?",
|
|
739
|
+
"sessions.mine.revokeAllOthers": "Alle anderen Geräte abmelden",
|
|
740
|
+
"sessions.mine.revokeAllOthers.confirm": "Alle Geräte außer diesem abmelden?",
|
|
732
741
|
"sessions:nav.sessionList": "Sitzungen",
|
|
733
742
|
"subscription-stripe.api-key": "Stripe API Key",
|
|
734
743
|
"subscription-stripe.billing-live": "Stripe Billing live",
|
|
@@ -746,15 +755,15 @@ export const localeDeBundle: Readonly<Record<string, string>> = {
|
|
|
746
755
|
"tags.manage.namePlaceholder": "Tag-Name",
|
|
747
756
|
"tags.manage.newLabel": "Neuer Tag",
|
|
748
757
|
"tags.manage.save": "Speichern",
|
|
749
|
-
"tags.manage.scopeLabel": "Geltungsbereich
|
|
750
|
-
"tags.manage.scopePlaceholder": "z. B. note
|
|
758
|
+
"tags.manage.scopeLabel": "Geltungsbereich. Leer lassen, damit der Tag überall gilt.",
|
|
759
|
+
"tags.manage.scopePlaceholder": "z. B. note",
|
|
751
760
|
"tags.manage.toggle": "Tag umschalten",
|
|
752
761
|
"tags.manage.usage": "{count}×",
|
|
753
762
|
"tags.picker.done": "Fertig",
|
|
754
763
|
"tags.picker.title": "Tags",
|
|
755
764
|
"tags.section.createMode": "Speichere zuerst den Eintrag, um Tags zu setzen.",
|
|
756
765
|
"tags.section.edit": "Tags bearbeiten",
|
|
757
|
-
"tags.section.empty": "
|
|
766
|
+
"tags.section.empty": "Noch keine Tags angelegt.",
|
|
758
767
|
"tags.section.loading": "Lädt…",
|
|
759
768
|
"tags.section.none": "Keine Tags",
|
|
760
769
|
"tags.section.working": "Speichert…",
|
|
@@ -764,7 +773,7 @@ export const localeDeBundle: Readonly<Record<string, string>> = {
|
|
|
764
773
|
"template-resolver.editor.loading": "Lädt aktuellen Stand…",
|
|
765
774
|
"template-resolver.editor.networkError": "Netzwerkfehler beim Speichern.",
|
|
766
775
|
"template-resolver.editor.readOnly":
|
|
767
|
-
"
|
|
776
|
+
"Nur lesbar. Änderungen erfordern die Rolle TenantAdmin oder SystemAdmin.",
|
|
768
777
|
"template-resolver.editor.save": "Speichern",
|
|
769
778
|
"template-resolver.editor.saveFailed": "Speichern fehlgeschlagen.",
|
|
770
779
|
"template-resolver.editor.saved": "Gespeichert.",
|
|
@@ -773,7 +782,7 @@ export const localeDeBundle: Readonly<Record<string, string>> = {
|
|
|
773
782
|
"tenant-settings.currency": "Standard-Währung",
|
|
774
783
|
"tenant-settings.locale": "Standard-Sprache",
|
|
775
784
|
"tenant-settings.settings": "Mandanten-Einstellungen",
|
|
776
|
-
"tenant-settings.settings.system": "
|
|
785
|
+
"tenant-settings.settings.system": "Standard-Mandanteneinstellungen",
|
|
777
786
|
"tenant.members.actions.editRoles": "Rollen bearbeiten",
|
|
778
787
|
"tenant.members.active": "Aktive Mitglieder",
|
|
779
788
|
"tenant.members.cancel": "Zurückziehen",
|
|
@@ -829,9 +838,9 @@ export const localeDeBundle: Readonly<Record<string, string>> = {
|
|
|
829
838
|
"tier-admin.error.generic": "Konnte das Tier nicht zuweisen.",
|
|
830
839
|
"tier-admin.error.load": "Mandanten konnten nicht geladen werden.",
|
|
831
840
|
"tier-admin.error.noTiers":
|
|
832
|
-
"
|
|
841
|
+
"Für diese App sind keine Tiers konfiguriert. Es gibt nichts, das sich zuweisen lässt.",
|
|
833
842
|
"tier-admin.explainer":
|
|
834
|
-
"Weise einem Mandanten ein Tier ohne Kauf zu.
|
|
843
|
+
"Weise einem Mandanten ein Tier ohne Kauf zu. Die manuelle Zuweisung bleibt bestehen, auch wenn sich die Abrechnung später automatisch aktualisiert.",
|
|
835
844
|
"tier-admin.submit": "Tier zuweisen",
|
|
836
845
|
"tier-admin.success": "Tier „{tier}“ zugewiesen.",
|
|
837
846
|
"tier-admin.tenant.label": "Mandant",
|
|
@@ -895,7 +904,7 @@ export const localeDeBundle: Readonly<Record<string, string>> = {
|
|
|
895
904
|
"userDataRights.errors.download.notFound":
|
|
896
905
|
"Der Download-Link ist ungültig oder gehört zu einem anderen Konto.",
|
|
897
906
|
"userDataRights.errors.download.signedUrlNotSupported":
|
|
898
|
-
"Der Download ist
|
|
907
|
+
"Der Download ist derzeit nicht möglich. Wir wurden bereits informiert, bitte versuche es später erneut.",
|
|
899
908
|
"userDataRights.errors.download.unavailable":
|
|
900
909
|
"Der Export ist noch nicht fertig oder fehlgeschlagen. Bitte prüfe den Status.",
|
|
901
910
|
"userDataRights.privacyCenter.deletion.cancel": "Löschung abbrechen",
|
|
@@ -907,7 +916,7 @@ export const localeDeBundle: Readonly<Record<string, string>> = {
|
|
|
907
916
|
"userDataRights.privacyCenter.deletion.explainer":
|
|
908
917
|
"Beantrage die Löschung deines Kontos. Bis zum Ablauf der Frist kannst du die Löschung wieder abbrechen.",
|
|
909
918
|
"userDataRights.privacyCenter.deletion.requested": "Dein Konto wird am {date} gelöscht.",
|
|
910
|
-
"userDataRights.privacyCenter.deletion.title": "Konto löschen
|
|
919
|
+
"userDataRights.privacyCenter.deletion.title": "Konto löschen · Art. 17",
|
|
911
920
|
"userDataRights.privacyCenter.errors.generic":
|
|
912
921
|
"Etwas ist schief gegangen. Bitte erneut versuchen.",
|
|
913
922
|
"userDataRights.privacyCenter.export.availableUntil": "Verfügbar bis {date}",
|
|
@@ -922,7 +931,7 @@ export const localeDeBundle: Readonly<Record<string, string>> = {
|
|
|
922
931
|
"userDataRights.privacyCenter.export.request": "Daten-Export anfordern",
|
|
923
932
|
"userDataRights.privacyCenter.export.requestNew": "Neuen Export anfordern",
|
|
924
933
|
"userDataRights.privacyCenter.export.requesting": "Wird angefordert …",
|
|
925
|
-
"userDataRights.privacyCenter.export.title": "Daten exportieren
|
|
934
|
+
"userDataRights.privacyCenter.export.title": "Daten exportieren · Art. 20",
|
|
926
935
|
"userDataRights.privacyCenter.intro":
|
|
927
936
|
"Verwalte deine Rechte nach DSGVO: Datenauskunft, Export, Einschränkung und Löschung deines Kontos.",
|
|
928
937
|
"userDataRights.privacyCenter.loadError": "Deine Daten konnten nicht geladen werden.",
|
|
@@ -931,10 +940,10 @@ export const localeDeBundle: Readonly<Record<string, string>> = {
|
|
|
931
940
|
"Du wirst sofort abgemeldet und kannst dich nicht mehr anmelden, bis der Support die Einschränkung aufhebt.",
|
|
932
941
|
"userDataRights.privacyCenter.restriction.dialogTitle": "Konto wirklich einschränken?",
|
|
933
942
|
"userDataRights.privacyCenter.restriction.explainer":
|
|
934
|
-
"
|
|
943
|
+
"Die Verarbeitung deiner Daten wird pausiert, und du wirst sofort abgemeldet. Nur der Support kann die Einschränkung danach wieder aufheben.",
|
|
935
944
|
"userDataRights.privacyCenter.restriction.restrict": "Konto einschränken",
|
|
936
945
|
"userDataRights.privacyCenter.restriction.restricted":
|
|
937
946
|
"Dein Konto ist eingeschränkt. Wende dich an den Support, um die Einschränkung aufzuheben.",
|
|
938
|
-
"userDataRights.privacyCenter.restriction.title": "Verarbeitung einschränken
|
|
947
|
+
"userDataRights.privacyCenter.restriction.title": "Verarbeitung einschränken · Art. 18",
|
|
939
948
|
"userDataRights.privacyCenter.title": "Datenschutz",
|
|
940
949
|
};
|
package/src/web.ts
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
|
+
import { type GermanAddress, germanBundleFor } from "./formal";
|
|
1
2
|
import { localeDeBundle } from "./strings";
|
|
2
3
|
|
|
4
|
+
export type { GermanAddress };
|
|
3
5
|
export { localeDeBundle };
|
|
4
6
|
|
|
5
|
-
export function localeDeClient(): {
|
|
7
|
+
export function localeDeClient(options?: { readonly address?: GermanAddress }): {
|
|
6
8
|
readonly name: "locale-de";
|
|
7
9
|
readonly translations: { readonly de: Readonly<Record<string, string>> };
|
|
8
10
|
} {
|
|
9
|
-
return {
|
|
11
|
+
return {
|
|
12
|
+
name: "locale-de",
|
|
13
|
+
translations: { de: germanBundleFor(options?.address ?? "informal") },
|
|
14
|
+
};
|
|
10
15
|
}
|