@escape-game-over/atlas 0.1.84 → 0.1.85
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/README.md +4 -0
- package/package.json +2 -2
- package/src/i18n/per-language.ts +45 -0
- package/src/site/define.ts +12 -0
package/README.md
CHANGED
|
@@ -109,6 +109,10 @@ export const { project, redirects } = atlas.project(baseMessages, baseRoutes, {.
|
|
|
109
109
|
export const site = atlas.site(baseMessages, baseRoutes, project)
|
|
110
110
|
```
|
|
111
111
|
|
|
112
|
+
A site that publishes one language under several regions writes the words once:
|
|
113
|
+
`atlas.perLanguage({ en: "Home", fr: "Accueil" })` fills in `en-GB`, `en-IE` and
|
|
114
|
+
`fr-FR` for one entry of `atlas.messages`, with placeholders still checked.
|
|
115
|
+
|
|
112
116
|
Step 4 returns `t()`, `rich()`, `plain()`, `pathFor()`, `urlFor()`, `fileUrl()`,
|
|
113
117
|
`localeLinksFor()`, `metaFor()`, `breadcrumbFor()`,
|
|
114
118
|
`getStaticPaths()`, `routes`, `sitemap()`, `robots()` and `llms()` already
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@escape-game-over/atlas",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.85",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Typed, data-driven machinery for static multi-locale, multi-deployment Astro sites.",
|
|
6
6
|
"private": false,
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"vite": ">= 8"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
|
-
"@biomejs/biome": "2.6.
|
|
61
|
+
"@biomejs/biome": "2.6.5",
|
|
62
62
|
"@types/node": "26.6.6",
|
|
63
63
|
"@vitest/coverage-istanbul": "5.1.0",
|
|
64
64
|
"astro": "7.4.5",
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** `"en"` for `"en-GB"`: everything before the first dash. */
|
|
2
|
+
export type LanguageOf<L extends string> =
|
|
3
|
+
L extends `${infer Language}-${string}` ? Language : never;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Every locale, holding its language's text.
|
|
7
|
+
*
|
|
8
|
+
* Keeps each string's literal type, so a message built this way has its
|
|
9
|
+
* placeholders checked exactly like one written out by hand.
|
|
10
|
+
*/
|
|
11
|
+
export type PerLanguage<L extends string, T> = {
|
|
12
|
+
readonly [K in L]: T[LanguageOf<K> & keyof T];
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* One message, written once per language and filled in for each of its tags.
|
|
17
|
+
*
|
|
18
|
+
* For a site that publishes one language under several regions, where `en-GB`
|
|
19
|
+
* and `en-IE` are different tags for `hreflang` but the same words. Checked here
|
|
20
|
+
* as well as in the types, because the cast below stops the compiler seeing
|
|
21
|
+
* this body, and a caller's `as any` would otherwise publish `undefined`.
|
|
22
|
+
*/
|
|
23
|
+
export function expandLanguages<L extends string, T>(
|
|
24
|
+
locales: readonly L[],
|
|
25
|
+
texts: T
|
|
26
|
+
): PerLanguage<L, T> {
|
|
27
|
+
const given = texts as Readonly<Record<string, string>>;
|
|
28
|
+
const languages = new Set(locales.map((tag) => tag.split("-")[0]));
|
|
29
|
+
for (const language of Object.keys(given)) {
|
|
30
|
+
if (!languages.has(language)) {
|
|
31
|
+
throw new Error(
|
|
32
|
+
`perLanguage: "${language}" is not a language this site declares.`
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const entries = locales.map((tag) => {
|
|
37
|
+
const language = tag.split("-")[0] as string;
|
|
38
|
+
const text = given[language];
|
|
39
|
+
if (text === undefined) {
|
|
40
|
+
throw new Error(`perLanguage: no text for "${language}" (${tag}).`);
|
|
41
|
+
}
|
|
42
|
+
return [tag, text];
|
|
43
|
+
});
|
|
44
|
+
return Object.fromEntries(entries) as PerLanguage<L, T>;
|
|
45
|
+
}
|
package/src/site/define.ts
CHANGED
|
@@ -10,6 +10,11 @@ import {
|
|
|
10
10
|
type NoExtraLocales,
|
|
11
11
|
type ValidateBase,
|
|
12
12
|
} from "../i18n/define.ts";
|
|
13
|
+
import {
|
|
14
|
+
expandLanguages,
|
|
15
|
+
type LanguageOf,
|
|
16
|
+
type PerLanguage,
|
|
17
|
+
} from "../i18n/per-language.ts";
|
|
13
18
|
import {
|
|
14
19
|
bindRedirects,
|
|
15
20
|
type DefinedProject,
|
|
@@ -86,6 +91,13 @@ export function defineSite<
|
|
|
86
91
|
return defineMessages(config, catalog as never) as T;
|
|
87
92
|
},
|
|
88
93
|
|
|
94
|
+
/** One message per language, filled in for each of that language's tags. */
|
|
95
|
+
perLanguage<const T extends Readonly<Record<LanguageOf<L>, string>>>(
|
|
96
|
+
texts: T & Readonly<Record<Exclude<keyof T, LanguageOf<L>>, never>>
|
|
97
|
+
): PerLanguage<L, T> {
|
|
98
|
+
return expandLanguages(Object.keys(config.locales) as L[], texts);
|
|
99
|
+
},
|
|
100
|
+
|
|
89
101
|
/** One deployment: what differs from the defaults. */
|
|
90
102
|
project<
|
|
91
103
|
const Catalog extends BaseCatalog<L>,
|