first commit

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
michilis
2026-08-20 22:41:25 +02:00
co-authored by Cursor
commit aa1771ea20
136 changed files with 27069 additions and 0 deletions
+83
View File
@@ -0,0 +1,83 @@
/**
* The locale table. Adding a language is this file, `xx.json`, and nothing else.
*
* Everything downstream reads from `LOCALES`: the Astro i18n config, the language
* switcher, the hreflang set, the sitemap, the suggestion bar and the catalog loader.
* There is deliberately no second list to keep in step.
*/
export const DEFAULT_LOCALE = 'en' as const;
export interface LocaleInfo {
/** URL prefix and catalog filename. Also the value of `<html lang>`. */
code: string;
/**
* The language's own name for itself, for the switcher. Never a flag: a flag is a
* country, and Spanish is not Spain.
*/
label: string;
/**
* BCP 47 tag handed to Intl. Region included because number and date formatting
* differ by region even when the language does not.
*/
intl: string;
/** `og:locale`. Underscored, region required by the OpenGraph spec. */
og: string;
}
/**
* Order matters: this is the order the switcher renders in, and the default locale
* comes first.
*/
export const LOCALES: LocaleInfo[] = [
{ code: 'en', label: 'English', intl: 'en-GB', og: 'en_US' },
{ code: 'es', label: 'Español', intl: 'es-ES', og: 'es_ES' },
{ code: 'nl', label: 'Nederlands', intl: 'nl-NL', og: 'nl_NL' },
];
export type Locale = string;
/** Just the codes, for the Astro config and for validating a URL segment. */
export const LOCALE_CODES: string[] = LOCALES.map((l) => l.code);
/** Every locale except the default, which is the set that carries a URL prefix. */
export const PREFIXED_LOCALES: string[] = LOCALE_CODES.filter((code) => code !== DEFAULT_LOCALE);
export function isLocale(value: unknown): value is Locale {
return typeof value === 'string' && LOCALE_CODES.includes(value);
}
export function localeInfo(locale: Locale): LocaleInfo {
return LOCALES.find((l) => l.code === locale) ?? LOCALES[0]!;
}
/** The Intl tag for a locale, for `Intl.NumberFormat` and friends. */
export function intlTag(locale: Locale): string {
return localeInfo(locale).intl;
}
/**
* Namespaces whose keys are sent to the browser.
*
* Islands render strings at runtime, so their copy has to reach the client. The static
* half of the site (home page sections, about, the legal pages) is rendered at build
* time and its copy never needs to ship. Splitting on the namespace keeps the inline
* payload to roughly a third of the catalog.
*
* `scripts/check-i18n.mjs` fails the build if an island reaches for a key outside this
* list, so the split cannot silently rot into a missing string on a real page.
*/
export const CLIENT_NAMESPACES = [
'common',
'time',
'status',
'pulse',
'mints',
'mint',
'nut',
'reviews',
'feed',
'login',
'signer',
'notfound',
] as const;