PocketBase gains optional, manually-authored English fields on articles and categories (same record, same slug), and the frontend serves an /en counterpart of every dynamic route via thin page wrappers around shared view components — no i18n library, consistent with the project's existing minimalism stance for a two-locale site with ~20 UI strings. An article/category with no translation 404s cleanly on its own /en detail page and is filtered out of /en listings, and the header's language-switch link falls back to the English blog index rather than a dead link; resolving that requires a global route middleware, since the layout's header renders before the page content in document order and so can't react to state a page component sets during its own async setup.
120 lines
5.0 KiB
TypeScript
120 lines
5.0 KiB
TypeScript
/**
|
|
* Static UI copy, in both languages. Deliberately a plain object, not a
|
|
* library: two locales, ~20 short strings, no plurals — see docs/frontend.md.
|
|
* Keyed by role (not by the Italian text), so a wording tweak never touches
|
|
* the key.
|
|
*/
|
|
import type { Locale } from './locale'
|
|
|
|
export const STRINGS = {
|
|
it: {
|
|
skipLink: 'Vai al contenuto',
|
|
tagline: 'Articoli, guide e analisi.',
|
|
navAriaLabel: 'Navigazione principale',
|
|
navHome: 'Home',
|
|
navBlog: 'Tutti gli articoli',
|
|
navCorralito: 'Come difendersi dal corralito',
|
|
footerNavAriaLabel: 'Naviga',
|
|
footerNavigaKicker: 'Naviga',
|
|
footerSeguiciKicker: 'Seguici',
|
|
footerFollowAriaLabel: 'Seguici',
|
|
contactLabel: 'Contatti:',
|
|
copyright: 'Tutti i diritti riservati.',
|
|
langSwitchLabel: 'English',
|
|
home: {
|
|
heading: "La fine dell'Unione Europea per come l'abbiamo conosciuta.",
|
|
img1Alt: 'Manifesti e scritte contro le banche durante una crisi bancaria',
|
|
img2Alt: "Bandiera dell'Unione Europea incrinata",
|
|
p1: "La fine della nostra moneta come moneta forte: la fine di quella moneta che ci ha dato la vera libertà di viaggiare, consumare e fare.",
|
|
p2: "Una fine che avverrà a seguito di un \"incidente\" nucleare, al quale farà seguito un corralito bancario, ossia l'impossibilità, da parte dei cittadini europei, di disporre liberamente dei propri depositi.",
|
|
p3: "Le monete fiduciarie sono per loro natura cannibalizzanti. Oggi tocca all'euro. Anche perché le altre hanno praticamente perso tutte tutto.",
|
|
fallbackKicker: 'Ultimo',
|
|
readLatest: "Leggi l'ultimo articolo",
|
|
metaDescription: 'Articoli, guide e analisi.',
|
|
},
|
|
blog: {
|
|
archiveKicker: "L'archivio",
|
|
title: 'Articoli',
|
|
titlePage: 'Articoli — pagina {page}',
|
|
metaDescription: 'Tutti gli articoli pubblicati sul blog.',
|
|
loadError: 'Non è stato possibile caricare gli articoli. Riprova più tardi.',
|
|
loading: 'Caricamento…',
|
|
empty: 'Non è stato ancora pubblicato nulla.',
|
|
prevPage: 'Pagina precedente',
|
|
nextPage: 'Pagina successiva',
|
|
pageOf: 'Pagina {current} di {total}',
|
|
paginationAriaLabel: 'Paginazione',
|
|
},
|
|
category: {
|
|
kicker: 'Categoria',
|
|
metaDescription: 'Tutti gli articoli nella categoria {name}.',
|
|
loadError: 'Non è stato possibile caricare gli articoli. Riprova più tardi.',
|
|
empty: 'Non ci sono articoli in questa categoria.',
|
|
},
|
|
article: {
|
|
breadcrumbAriaLabel: 'Percorso di navigazione',
|
|
articlesCrumb: 'Articoli',
|
|
byAuthor: 'Di {author}',
|
|
backToArticles: 'Torna a tutti gli articoli',
|
|
},
|
|
},
|
|
en: {
|
|
skipLink: 'Skip to content',
|
|
tagline: 'Articles, guides and analysis.',
|
|
navAriaLabel: 'Main navigation',
|
|
navHome: 'Home',
|
|
navBlog: 'All articles',
|
|
navCorralito: '',
|
|
footerNavAriaLabel: 'Navigate',
|
|
footerNavigaKicker: 'Navigate',
|
|
footerSeguiciKicker: 'Follow us',
|
|
footerFollowAriaLabel: 'Follow us',
|
|
contactLabel: 'Contact:',
|
|
copyright: 'All rights reserved.',
|
|
langSwitchLabel: 'Italiano',
|
|
home: {
|
|
heading: "The end of the European Union as we've known it.",
|
|
img1Alt: 'Banners and graffiti against banks during a banking crisis',
|
|
img2Alt: 'Cracked European Union flag',
|
|
p1: 'The end of our currency as a strong currency: the end of the currency that gave us the real freedom to travel, spend and act.',
|
|
p2: 'An end that will follow a nuclear "incident", followed by a banking corralito — the impossibility, for European citizens, of freely accessing their own deposits.',
|
|
p3: "Fiat currencies are cannibalizing by nature. Today it's the euro's turn. Especially since the others have already lost almost everything.",
|
|
fallbackKicker: 'Latest',
|
|
readLatest: 'Read the latest article',
|
|
metaDescription: 'Articles, guides and analysis.',
|
|
},
|
|
blog: {
|
|
archiveKicker: 'The archive',
|
|
title: 'Articles',
|
|
titlePage: 'Articles — page {page}',
|
|
metaDescription: 'All the articles published on the blog.',
|
|
loadError: 'Could not load the articles. Please try again later.',
|
|
loading: 'Loading…',
|
|
empty: 'Nothing has been published yet.',
|
|
prevPage: 'Previous page',
|
|
nextPage: 'Next page',
|
|
pageOf: 'Page {current} of {total}',
|
|
paginationAriaLabel: 'Pagination',
|
|
},
|
|
category: {
|
|
kicker: 'Category',
|
|
metaDescription: 'All the articles in the {name} category.',
|
|
loadError: 'Could not load the articles. Please try again later.',
|
|
empty: 'There are no articles in this category.',
|
|
},
|
|
article: {
|
|
breadcrumbAriaLabel: 'Breadcrumb',
|
|
articlesCrumb: 'Articles',
|
|
byAuthor: 'By {author}',
|
|
backToArticles: 'Back to all articles',
|
|
},
|
|
},
|
|
} as const satisfies Record<Locale, unknown>
|
|
|
|
/** Fills `{placeholder}` tokens in a translated string. */
|
|
export function interpolate(template: string, params: Record<string, string | number>): string {
|
|
return template.replace(/\{(\w+)\}/g, (match, key: string) =>
|
|
key in params ? String(params[key]) : match
|
|
)
|
|
}
|