source: Klonkt/src/services/i18n.js@ 137cca5

main
Last change on this file since 137cca5 was 137cca5, checked in by roboburr <roboburr@…>, 3 months ago

feat(i18n phase 3): welcome/first-run, login, register, bottom-tab translated

NL/EN/DE keys + views: welcome.ejs, auth-register.ejs, auth-login.ejs (common
strings; Google error map still NL), bottom-tab.ejs. The most visible visitor
pages of a fresh (German) site are now translated.

Co-Authored-By: Claude <noreply@…>

  • Property mode set to 100644
File size: 14.7 KB
Line 
1// i18n — eenvoudige interface-vertaling (UI-strings), bezoeker-instelbaar.
2//
3// Taalkeuze: req.session.lang (gezet via /lang/:code) → anders browser-taal
4// (Accept-Language) → anders 'nl'. De helper t(lang, key, vars) zoekt de string op
5// in DICT[lang], valt terug op 'nl', dan op de key zelf. Alleen INTERFACE-teksten;
6// door gebruikers geschreven content (posts, sitenaam, bio) wordt niet vertaald.
7
8export const SUPPORTED = ['nl', 'en', 'de'];
9export const LANG_NAMES = { nl: 'Nederlands', en: 'English', de: 'Deutsch' };
10
11const DICT = {
12 nl: {
13 'nav.back_to_site': '← Terug naar site',
14 'nav.home': 'Home',
15 'nav.archive': 'Archief',
16 'nav.search': 'Zoeken',
17 'nav.theme': 'Thema wisselen',
18 'nav.install': 'App installeren',
19 'nav.login': 'Inloggen',
20 'nav.logout': 'Uitloggen',
21 'nav.admin': 'Beheer',
22 'nav.account': 'Account',
23 'nav.profile': 'Profiel',
24 'nav.favorites': 'Favorieten',
25 'nav.new_post': 'Nieuwe post',
26 'nav.language': 'Taal',
27 'switch.agenda': 'Agenda',
28 'switch.solo': 'Solo',
29 'switch.circle': 'Cirkel',
30 'switch.timeline': 'Tijdlijn',
31 'switch.grid': 'Grid',
32 'postnav.newer': 'Nieuwer',
33 'postnav.older': 'Ouder',
34 'postnav.newest': 'Nieuwste post',
35 'postnav.oldest': 'Oudste post',
36 'footer.subscribe_cta': 'Blijf op de hoogte',
37 'footer.subscribe': 'Inschrijven',
38 'footer.install': 'Installeer app',
39 'common.email_placeholder': 'jouw@email.nl',
40 'common.back_to_admin': '← Beheer',
41 // Agenda (publiek)
42 'agenda.title': 'Agenda',
43 'agenda.empty': 'Geen aangekondigde evenementen op dit moment.',
44 'agenda.tickets': 'Tickets',
45 'agenda.notify_h': 'Mis geen evenement',
46 'agenda.notify_sub': 'Laat je e-mail achter en je krijgt een seintje bij een nieuw evenement. Uitschrijven kan altijd.',
47 'agenda.notify_btn': 'Houd me op de hoogte',
48 'agenda.msg_done': '✓ Je staat op de lijst — je hoort het zodra er een evenement wordt aangekondigd.',
49 'agenda.msg_check': '✉ Check je mail om je aanmelding te bevestigen.',
50 // Downloads (publiek)
51 'downloads.title': 'Downloads',
52 'downloads.sub': 'Gratis te downloaden — laat je e-mailadres achter en je krijgt het bestand.',
53 'downloads.empty': 'Er staan momenteel geen downloads klaar.',
54 'downloads.btn': '⬇ Download',
55 // Beheer-dashboard
56 'admin.title': 'Beheer',
57 'admin.tagline_solo': 'Solo-modus — jouw site.',
58 'admin.tagline_hub': 'Hub-modus — bedrijfssite met gebruikers, elk hun eigen Klonkt Hub.',
59 'admin.b_sites': '🌐 Sites', 'admin.b_users': '👥 Gebruikers', 'admin.b_audio': '🎵 Audio',
60 'admin.b_playlists': '📃 Playlists', 'admin.b_comments': '💬 Reacties', 'admin.b_seo': '🔎 SEO',
61 'admin.b_settings': '⚙️ Instellingen', 'admin.b_newpost': '✍️ Nieuwe post', 'admin.b_look': '🎨 Uiterlijk',
62 'admin.b_makesite': '🎨 Maak je site aan', 'admin.b_circle': '🔗 Cirkel', 'admin.b_stats': '📊 Statistieken',
63 'admin.b_newsletter': '✉️ Nieuwsbrief', 'admin.b_perskit': '📰 Perskit', 'admin.b_downloads': '⬇ Downloads',
64 'admin.b_linkbio': '🔗 Link-in-bio', 'admin.b_agenda': '📅 Agenda', 'admin.b_google': '🔑 Google',
65 'admin.b_updates': '🔄 Updates', 'admin.b_help': '📖 Handleiding',
66 'admin.st_users': 'Gebruikers', 'admin.st_sites': 'Sites', 'admin.st_posts': 'Posts', 'admin.st_published': 'Gepubliceerd',
67 'admin.sec_posts': 'Posts', 'admin.sec_sites': 'Sites', 'admin.sec_users': 'Users',
68 'admin.draft': 'Concept', 'admin.edit': 'Bewerken', 'admin.view': 'Bekijk',
69 'admin.th_slug': 'Slug', 'admin.th_title': 'Titel', 'admin.th_owner': 'Eigenaar', 'admin.th_created': 'Aangemaakt',
70 'admin.th_username': 'Gebruikersnaam', 'admin.th_email': 'E-mail', 'admin.th_role': 'Rol', 'admin.th_joined': 'Lid sinds',
71 // Welkom / first-run
72 'welcome.title': 'Welkom bij Klonkt',
73 'welcome.tagline': 'Een zelf-gehost publicatieplatform.',
74 'welcome.have_account': 'Al een account? Inloggen',
75 'welcome.note': 'Maak je beheerdersaccount aan om te beginnen — daarna is registratie gesloten.',
76 'welcome.nosite': 'Hoi {user}! Er is nog geen site ingesteld.',
77 // Auth (login/register)
78 'auth.admin_login_title': 'Beheerder inloggen',
79 'auth.username_or_email': 'Gebruikersnaam of e-mail',
80 'auth.password': 'Wachtwoord',
81 'auth.forgot': 'Wachtwoord vergeten?',
82 'auth.google_btn': 'Inloggen met Google',
83 'auth.public_sub': 'Log in om te reageren en je favorieten te bewaren.',
84 'auth.admin_box_q': 'Beheerder?',
85 'auth.admin_box_sub': 'Log in met gebruikersnaam & wachtwoord',
86 'auth.google_unavail': 'Inloggen met Google is op deze site nog niet ingesteld.',
87 'auth.create_admin': 'Beheerder aanmaken',
88 'auth.reg_intro': 'Eerste keer opzetten — maak je beheerdersaccount aan. Dit kan maar één keer.',
89 'auth.f_username': 'Gebruikersnaam (3-32 tekens, letters/cijfers/_-)',
90 'auth.f_email': 'E-mail',
91 'auth.f_password': 'Wachtwoord (min 8 tekens)',
92 // Bottom-tab (mobiel)
93 'tab.home': 'Home', 'tab.search': 'Zoek', 'tab.write': 'Schrijven', 'tab.profile': 'Profiel',
94 },
95 en: {
96 'nav.back_to_site': '← Back to site',
97 'nav.home': 'Home',
98 'nav.archive': 'Archive',
99 'nav.search': 'Search',
100 'nav.theme': 'Toggle theme',
101 'nav.install': 'Install app',
102 'nav.login': 'Log in',
103 'nav.logout': 'Log out',
104 'nav.admin': 'Admin',
105 'nav.account': 'Account',
106 'nav.profile': 'Profile',
107 'nav.favorites': 'Favorites',
108 'nav.new_post': 'New post',
109 'nav.language': 'Language',
110 'switch.agenda': 'Agenda',
111 'switch.solo': 'Solo',
112 'switch.circle': 'Circle',
113 'switch.timeline': 'Timeline',
114 'switch.grid': 'Grid',
115 'postnav.newer': 'Newer',
116 'postnav.older': 'Older',
117 'postnav.newest': 'Newest post',
118 'postnav.oldest': 'Oldest post',
119 'footer.subscribe_cta': 'Stay in the loop',
120 'footer.subscribe': 'Subscribe',
121 'footer.install': 'Install app',
122 'common.email_placeholder': 'you@email.com',
123 'common.back_to_admin': '← Admin',
124 'agenda.title': 'Agenda',
125 'agenda.empty': 'No announced events at the moment.',
126 'agenda.tickets': 'Tickets',
127 'agenda.notify_h': 'Never miss an event',
128 'agenda.notify_sub': 'Leave your email and we’ll let you know about new events. Unsubscribe any time.',
129 'agenda.notify_btn': 'Keep me posted',
130 'agenda.msg_done': '✓ You’re on the list — we’ll let you know when an event is announced.',
131 'agenda.msg_check': '✉ Check your email to confirm your sign-up.',
132 'downloads.title': 'Downloads',
133 'downloads.sub': 'Free to download — leave your email and you’ll get the file.',
134 'downloads.empty': 'No downloads available right now.',
135 'downloads.btn': '⬇ Download',
136 'admin.title': 'Admin',
137 'admin.tagline_solo': 'Solo mode — your site.',
138 'admin.tagline_hub': 'Hub mode — a company site with users, each their own Klonkt Hub.',
139 'admin.b_sites': '🌐 Sites', 'admin.b_users': '👥 Users', 'admin.b_audio': '🎵 Audio',
140 'admin.b_playlists': '📃 Playlists', 'admin.b_comments': '💬 Comments', 'admin.b_seo': '🔎 SEO',
141 'admin.b_settings': '⚙️ Settings', 'admin.b_newpost': '✍️ New post', 'admin.b_look': '🎨 Appearance',
142 'admin.b_makesite': '🎨 Create your site', 'admin.b_circle': '🔗 Circle', 'admin.b_stats': '📊 Statistics',
143 'admin.b_newsletter': '✉️ Newsletter', 'admin.b_perskit': '📰 Press kit', 'admin.b_downloads': '⬇ Downloads',
144 'admin.b_linkbio': '🔗 Link-in-bio', 'admin.b_agenda': '📅 Agenda', 'admin.b_google': '🔑 Google',
145 'admin.b_updates': '🔄 Updates', 'admin.b_help': '📖 Manual',
146 'admin.st_users': 'Users', 'admin.st_sites': 'Sites', 'admin.st_posts': 'Posts', 'admin.st_published': 'Published',
147 'admin.sec_posts': 'Posts', 'admin.sec_sites': 'Sites', 'admin.sec_users': 'Users',
148 'admin.draft': 'Draft', 'admin.edit': 'Edit', 'admin.view': 'View',
149 'admin.th_slug': 'Slug', 'admin.th_title': 'Title', 'admin.th_owner': 'Owner', 'admin.th_created': 'Created',
150 'admin.th_username': 'Username', 'admin.th_email': 'Email', 'admin.th_role': 'Role', 'admin.th_joined': 'Joined',
151 'welcome.title': 'Welcome to Klonkt',
152 'welcome.tagline': 'A self-hosted publishing platform.',
153 'welcome.have_account': 'Already have an account? Log in',
154 'welcome.note': 'Create your admin account to get started — registration closes after that.',
155 'welcome.nosite': 'Hi {user}! No site is configured yet.',
156 'auth.admin_login_title': 'Admin login',
157 'auth.username_or_email': 'Username or email',
158 'auth.password': 'Password',
159 'auth.forgot': 'Forgot password?',
160 'auth.google_btn': 'Sign in with Google',
161 'auth.public_sub': 'Log in to comment and save your favorites.',
162 'auth.admin_box_q': 'Admin?',
163 'auth.admin_box_sub': 'Log in with username & password',
164 'auth.google_unavail': 'Google sign-in isn’t set up on this site yet.',
165 'auth.create_admin': 'Create admin',
166 'auth.reg_intro': 'First-time setup — create your admin account. This can only be done once.',
167 'auth.f_username': 'Username (3-32 chars, letters/digits/_-)',
168 'auth.f_email': 'Email',
169 'auth.f_password': 'Password (min 8 chars)',
170 'tab.home': 'Home', 'tab.search': 'Search', 'tab.write': 'Write', 'tab.profile': 'Profile',
171 },
172 de: {
173 'nav.back_to_site': '← Zurück zur Seite',
174 'nav.home': 'Start',
175 'nav.archive': 'Archiv',
176 'nav.search': 'Suche',
177 'nav.theme': 'Thema wechseln',
178 'nav.install': 'App installieren',
179 'nav.login': 'Anmelden',
180 'nav.logout': 'Abmelden',
181 'nav.admin': 'Verwaltung',
182 'nav.account': 'Konto',
183 'nav.profile': 'Profil',
184 'nav.favorites': 'Favoriten',
185 'nav.new_post': 'Neuer Beitrag',
186 'nav.language': 'Sprache',
187 'switch.agenda': 'Termine',
188 'switch.solo': 'Solo',
189 'switch.circle': 'Kreis',
190 'switch.timeline': 'Zeitleiste',
191 'switch.grid': 'Raster',
192 'postnav.newer': 'Neuer',
193 'postnav.older': 'Älter',
194 'postnav.newest': 'Neuester Beitrag',
195 'postnav.oldest': 'Ältester Beitrag',
196 'footer.subscribe_cta': 'Bleib auf dem Laufenden',
197 'footer.subscribe': 'Abonnieren',
198 'footer.install': 'App installieren',
199 'common.email_placeholder': 'du@email.de',
200 'common.back_to_admin': '← Verwaltung',
201 'agenda.title': 'Termine',
202 'agenda.empty': 'Zurzeit keine angekündigten Termine.',
203 'agenda.tickets': 'Tickets',
204 'agenda.notify_h': 'Keinen Termin verpassen',
205 'agenda.notify_sub': 'Hinterlasse deine E-Mail und wir melden uns bei neuen Terminen. Abmelden jederzeit möglich.',
206 'agenda.notify_btn': 'Halt mich auf dem Laufenden',
207 'agenda.msg_done': '✓ Du stehst auf der Liste — wir melden uns, sobald ein Termin angekündigt wird.',
208 'agenda.msg_check': '✉ Prüfe deine E-Mail, um die Anmeldung zu bestätigen.',
209 'downloads.title': 'Downloads',
210 'downloads.sub': 'Kostenlos herunterladen — hinterlasse deine E-Mail und du bekommst die Datei.',
211 'downloads.empty': 'Momentan keine Downloads verfügbar.',
212 'downloads.btn': '⬇ Herunterladen',
213 'admin.title': 'Verwaltung',
214 'admin.tagline_solo': 'Solo-Modus — deine Seite.',
215 'admin.tagline_hub': 'Hub-Modus — eine Firmenseite mit Nutzern, je ein eigener Klonkt Hub.',
216 'admin.b_sites': '🌐 Seiten', 'admin.b_users': '👥 Nutzer', 'admin.b_audio': '🎵 Audio',
217 'admin.b_playlists': '📃 Playlists', 'admin.b_comments': '💬 Kommentare', 'admin.b_seo': '🔎 SEO',
218 'admin.b_settings': '⚙️ Einstellungen', 'admin.b_newpost': '✍️ Neuer Beitrag', 'admin.b_look': '🎨 Aussehen',
219 'admin.b_makesite': '🎨 Seite erstellen', 'admin.b_circle': '🔗 Kreis', 'admin.b_stats': '📊 Statistik',
220 'admin.b_newsletter': '✉️ Newsletter', 'admin.b_perskit': '📰 Pressekit', 'admin.b_downloads': '⬇ Downloads',
221 'admin.b_linkbio': '🔗 Link-in-Bio', 'admin.b_agenda': '📅 Termine', 'admin.b_google': '🔑 Google',
222 'admin.b_updates': '🔄 Updates', 'admin.b_help': '📖 Anleitung',
223 'admin.st_users': 'Nutzer', 'admin.st_sites': 'Seiten', 'admin.st_posts': 'Beiträge', 'admin.st_published': 'Veröffentlicht',
224 'admin.sec_posts': 'Beiträge', 'admin.sec_sites': 'Seiten', 'admin.sec_users': 'Nutzer',
225 'admin.draft': 'Entwurf', 'admin.edit': 'Bearbeiten', 'admin.view': 'Ansehen',
226 'admin.th_slug': 'Slug', 'admin.th_title': 'Titel', 'admin.th_owner': 'Eigentümer', 'admin.th_created': 'Erstellt',
227 'admin.th_username': 'Benutzername', 'admin.th_email': 'E-Mail', 'admin.th_role': 'Rolle', 'admin.th_joined': 'Mitglied seit',
228 'welcome.title': 'Willkommen bei Klonkt',
229 'welcome.tagline': 'Eine selbstgehostete Publishing-Plattform.',
230 'welcome.have_account': 'Schon ein Konto? Anmelden',
231 'welcome.note': 'Lege dein Administrator-Konto an, um zu starten — danach ist die Registrierung geschlossen.',
232 'welcome.nosite': 'Hallo {user}! Es ist noch keine Seite eingerichtet.',
233 'auth.admin_login_title': 'Administrator-Anmeldung',
234 'auth.username_or_email': 'Benutzername oder E-Mail',
235 'auth.password': 'Passwort',
236 'auth.forgot': 'Passwort vergessen?',
237 'auth.google_btn': 'Mit Google anmelden',
238 'auth.public_sub': 'Melde dich an, um zu kommentieren und Favoriten zu speichern.',
239 'auth.admin_box_q': 'Administrator?',
240 'auth.admin_box_sub': 'Mit Benutzername & Passwort anmelden',
241 'auth.google_unavail': 'Google-Anmeldung ist auf dieser Seite noch nicht eingerichtet.',
242 'auth.create_admin': 'Administrator anlegen',
243 'auth.reg_intro': 'Ersteinrichtung — lege dein Administrator-Konto an. Das geht nur einmal.',
244 'auth.f_username': 'Benutzername (3-32 Zeichen, Buchstaben/Ziffern/_-)',
245 'auth.f_email': 'E-Mail',
246 'auth.f_password': 'Passwort (min. 8 Zeichen)',
247 'tab.home': 'Start', 'tab.search': 'Suche', 'tab.write': 'Schreiben', 'tab.profile': 'Profil',
248 },
249};
250
251export function t(lang, key, vars) {
252 const l = SUPPORTED.includes(lang) ? lang : 'nl';
253 let s = (DICT[l] && DICT[l][key]);
254 if (s === undefined) s = (DICT.nl[key] !== undefined ? DICT.nl[key] : key);
255 if (vars) for (const k in vars) s = s.replace(new RegExp('\\{' + k + '\\}', 'g'), vars[k]);
256 return s;
257}
258
259// Bepaal de taal voor dit request: expliciete sessie-keuze → instance-standaard
260// (env KLONKT_DEFAULT_LANG, bv. 'de' voor een Duitse site) → browser-taal → nl.
261export function resolveLang(req) {
262 const s = req && req.session && req.session.lang;
263 if (s && SUPPORTED.includes(s)) return s; // bezoeker koos zelf
264 const envDefault = (process.env.KLONKT_DEFAULT_LANG || '').toLowerCase();
265 if (SUPPORTED.includes(envDefault)) return envDefault; // per-instance standaard
266 const al = ((req && req.headers && req.headers['accept-language']) || '').toLowerCase();
267 const first = al.split(',')[0].trim().slice(0, 2);
268 if (SUPPORTED.includes(first)) return first; // browser-voorkeur
269 return 'nl';
270}
Note: See TracBrowser for help on using the repository browser.