feat: i18n layer — locales, slug-map contract, typed dictionaries
- locales.ts: locale codes, html lang, hreflang, OG locale - slugMap.ts: EN/NO route table + section anchors, twin-path resolver (the tested contract) - typed EN/NO dictionaries implementing one interface (missing key = compile error) - useDict accessor + interpolate helper; no runtime i18n library Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
import type { Dictionary } from './dictionary';
|
||||
|
||||
export const en: Dictionary = {
|
||||
nav: {
|
||||
home: 'Home',
|
||||
projects: 'Projects',
|
||||
experience: 'Experience',
|
||||
about: 'About',
|
||||
contact: 'Contact',
|
||||
},
|
||||
lang: {
|
||||
switchTo: 'Switch to Norwegian',
|
||||
en: 'EN',
|
||||
no: 'NO',
|
||||
},
|
||||
theme: {
|
||||
toLight: 'Switch to light mode',
|
||||
toDark: 'Switch to dark mode',
|
||||
},
|
||||
cv: {
|
||||
download: 'Download CV',
|
||||
english: 'English CV',
|
||||
norsk: 'Norwegian CV',
|
||||
fileMeta: 'pdf · {size} kB',
|
||||
},
|
||||
project: {
|
||||
readCaseStudy: 'Read case study',
|
||||
explore: 'Explore',
|
||||
all: 'All projects',
|
||||
caseStudyEyebrow: 'Case study · personal product',
|
||||
capabilityEyebrow: 'Capability · infrastructure',
|
||||
onThisPage: 'On this page',
|
||||
repository: 'Repository',
|
||||
liveDemo: 'Live demo',
|
||||
prev: 'Previous',
|
||||
next: 'Next',
|
||||
status: {
|
||||
active: 'Active',
|
||||
'in-development': 'In development',
|
||||
archived: 'Archived',
|
||||
},
|
||||
tldr: { what: 'What', why: 'Why', stack: 'Stack', role: 'Role' },
|
||||
},
|
||||
form: {
|
||||
name: 'Name',
|
||||
email: 'E-mail',
|
||||
message: 'Message',
|
||||
send: 'Send message',
|
||||
sending: 'Sending',
|
||||
successTitle: 'Message sent',
|
||||
successBody: 'Thanks — I usually reply within a day.',
|
||||
errorTitle: 'Something went wrong',
|
||||
errorBody: 'The message could not be sent. Please e-mail me directly instead.',
|
||||
required: 'This field is required',
|
||||
invalidEmail: 'Please enter a valid e-mail address',
|
||||
orEmail: 'Or reach me directly',
|
||||
copy: 'Copy',
|
||||
copied: 'Copied',
|
||||
},
|
||||
// The first-visit hint appears only on EN pages, inviting a Norwegian-preferring
|
||||
// visitor to switch — so its text is written in Norwegian (ROUTING_SPEC §3).
|
||||
hint: {
|
||||
text: 'Denne siden finnes på norsk',
|
||||
action: 'Bytt til norsk',
|
||||
dismiss: 'Lukk',
|
||||
},
|
||||
notFound: {
|
||||
code: 'route not found',
|
||||
title: 'No such page',
|
||||
body: 'The page you were looking for does not exist or has moved.',
|
||||
home: 'Go to homepage',
|
||||
projects: 'View projects',
|
||||
},
|
||||
a11y: {
|
||||
skipToContent: 'Skip to content',
|
||||
primaryNav: 'Primary',
|
||||
openMenu: 'Open menu',
|
||||
closeMenu: 'Close menu',
|
||||
breadcrumb: 'Breadcrumb',
|
||||
backToTop: 'Back to top',
|
||||
},
|
||||
footer: {
|
||||
tagline: 'Systems developer based in Tønsberg, Norway.',
|
||||
colophon: 'How this site works',
|
||||
availability: 'Open to remote, hybrid or on-site roles.',
|
||||
copyright: '© {year} Connor Babbington',
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user