rosetta-i18n 0.2.1 → 1.0.0-beta.2

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.
Files changed (42) hide show
  1. package/README.md +595 -141
  2. package/dist/esm/astro.js +103 -0
  3. package/dist/esm/astro.js.map +1 -0
  4. package/dist/esm/cli.js +2544 -104
  5. package/dist/esm/cli.js.map +1 -1
  6. package/dist/esm/config-BePhmTW6.js +497 -0
  7. package/dist/esm/config-BePhmTW6.js.map +1 -0
  8. package/dist/esm/config.js +4 -0
  9. package/dist/esm/config.js.map +1 -0
  10. package/dist/esm/index.js +1212 -83
  11. package/dist/esm/index.js.map +1 -1
  12. package/dist/esm/locale.js +95 -0
  13. package/dist/esm/locale.js.map +1 -0
  14. package/dist/esm/next.js +712 -84
  15. package/dist/esm/next.js.map +1 -1
  16. package/dist/esm/react.js +150 -0
  17. package/dist/esm/react.js.map +1 -0
  18. package/dist/types/cli.d.ts +28 -45
  19. package/dist/types/config.d.ts +2 -42
  20. package/dist/types/index.d.ts +8 -28
  21. package/dist/types/legacy.d.ts +50 -0
  22. package/dist/types/locale/astro.d.ts +33 -0
  23. package/dist/types/locale/core.d.ts +102 -0
  24. package/dist/types/locale/next.d.ts +56 -0
  25. package/dist/types/next.d.ts +4 -3
  26. package/dist/types/project/config.d.ts +78 -0
  27. package/dist/types/project/entries.d.ts +22 -0
  28. package/dist/types/project/glob.d.ts +18 -0
  29. package/dist/types/project/init.d.ts +36 -0
  30. package/dist/types/project/jsonc.d.ts +18 -0
  31. package/dist/types/project/lock.d.ts +13 -0
  32. package/dist/types/project/paths.d.ts +16 -0
  33. package/dist/types/project/plan.d.ts +45 -0
  34. package/dist/types/project/workflow.d.ts +112 -0
  35. package/dist/types/prompt.d.ts +11 -7
  36. package/dist/types/react.d.ts +59 -0
  37. package/dist/types/rosetta.d.ts +51 -0
  38. package/dist/types/types.d.ts +80 -0
  39. package/dist/types/validate.d.ts +29 -0
  40. package/package.json +29 -4
  41. package/schema/config.json +138 -0
  42. package/skills/rosetta/SKILL.md +319 -0
@@ -0,0 +1,103 @@
1
+ const LOCALE_HEADER = "x-rosetta-locale";
2
+ const DEFAULT_LOCALE_COOKIE = "locale";
3
+ const ONE_YEAR_SECONDS = 60 * 60 * 24 * 365;
4
+ function localeCookieName(config) {
5
+ return config.cookieName ?? DEFAULT_LOCALE_COOKIE;
6
+ }
7
+ function localeCookieOptions(config) {
8
+ return {
9
+ path: "/",
10
+ maxAge: config.cookieMaxAgeSeconds ?? ONE_YEAR_SECONDS,
11
+ sameSite: "lax"
12
+ };
13
+ }
14
+ function isLocale(config, value) {
15
+ return typeof value === "string" && config.locales.includes(value);
16
+ }
17
+ function localeFromPath(config, pathname) {
18
+ const segment = pathname.split("/")[1];
19
+ return segment && isLocale(config, segment) ? segment : null;
20
+ }
21
+ function stripLocale(config, pathname) {
22
+ const locale = localeFromPath(config, pathname);
23
+ if (!locale) return { locale: null, path: pathname || "/" };
24
+ const rest = pathname.slice(locale.length + 1);
25
+ return { locale, path: rest || "/" };
26
+ }
27
+ function resolveLocale(config, input) {
28
+ const fromPath = localeFromPath(config, input.pathname);
29
+ if (fromPath) {
30
+ return {
31
+ locale: fromPath,
32
+ path: stripLocale(config, input.pathname).path,
33
+ source: "url"
34
+ };
35
+ }
36
+ if (isLocale(config, input.header)) {
37
+ return { locale: input.header, path: input.pathname, source: "header" };
38
+ }
39
+ if (isLocale(config, input.cookie)) {
40
+ return { locale: input.cookie, path: input.pathname, source: "cookie" };
41
+ }
42
+ return {
43
+ locale: config.defaultLocale,
44
+ path: input.pathname,
45
+ source: "default"
46
+ };
47
+ }
48
+ function serializeLocaleCookie(config, locale) {
49
+ const options = localeCookieOptions(config);
50
+ return `${localeCookieName(config)}=${encodeURIComponent(locale)}; Path=${options.path}; Max-Age=${options.maxAge}; SameSite=${capitalize(options.sameSite)}`;
51
+ }
52
+ function parseCookie(header, name) {
53
+ if (!header) return null;
54
+ for (const part of header.split(";")) {
55
+ const [key, ...rest] = part.trim().split("=");
56
+ if (key === name) return decodeURIComponent(rest.join("="));
57
+ }
58
+ return null;
59
+ }
60
+ function capitalize(value) {
61
+ return value.charAt(0).toUpperCase() + value.slice(1);
62
+ }
63
+
64
+ function createAstroLocaleMiddleware(config) {
65
+ return async function onRequest(context, next) {
66
+ const url = new URL(context.request.url);
67
+ const resolved = resolveLocale(config, {
68
+ pathname: url.pathname,
69
+ header: context.request.headers.get(LOCALE_HEADER),
70
+ cookie: parseCookie(
71
+ context.request.headers.get("cookie"),
72
+ localeCookieName(config)
73
+ )
74
+ });
75
+ context.locals.locale = resolved.locale;
76
+ if (resolved.source === "url") {
77
+ const target = new URL(url);
78
+ target.pathname = resolved.path;
79
+ const response2 = await context.rewrite(target);
80
+ response2.headers.set(LOCALE_HEADER, resolved.locale);
81
+ response2.headers.append(
82
+ "Set-Cookie",
83
+ serializeLocaleCookie(config, resolved.locale)
84
+ );
85
+ return response2;
86
+ }
87
+ const response = await next();
88
+ if (resolved.source === "cookie" || resolved.source === "default") {
89
+ response.headers.append(
90
+ "Set-Cookie",
91
+ serializeLocaleCookie(config, resolved.locale)
92
+ );
93
+ }
94
+ return response;
95
+ };
96
+ }
97
+ function getAstroLocale(context, config) {
98
+ const locale = context.locals.locale;
99
+ return isLocale(config, locale) ? locale : config.defaultLocale;
100
+ }
101
+
102
+ export { createAstroLocaleMiddleware, getAstroLocale };
103
+ //# sourceMappingURL=astro.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"astro.js","sources":["../../src/locale/core.ts","../../src/locale/astro.ts"],"sourcesContent":["/**\n * Runtime locale layer — framework-agnostic core.\n *\n * The translation engine answers \"how do I translate these strings?\". This\n * module answers \"which locale is this request in, and how do I switch?\" — the\n * routing + cookie model that the React, Next, and Astro adapters are thin\n * wrappers around.\n *\n * The config is deliberately plain data (no functions) so the exact same object\n * can cross the server → client boundary. Resolution is deterministic and\n * one-directional:\n *\n * 1. a locale-prefixed URL (`/ja/pricing`) is authoritative,\n * 2. then a forced header (`x-rosetta-locale`, set by middleware),\n * 3. then the persisted cookie,\n * 4. then the default locale.\n *\n * Switching sets the cookie *and* navigates to the localized URL, so the URL and\n * the cookie always agree. There is no client-side guessing, no\n * refresh-after-push race, and no way for a stale request to re-pin the locale.\n */\n\nexport const LOCALE_HEADER = \"x-rosetta-locale\";\nexport const DEFAULT_LOCALE_COOKIE = \"locale\";\nexport const ONE_YEAR_SECONDS = 60 * 60 * 24 * 365;\n\nexport interface LocaleConfig {\n\t/** Every locale, including the default. Order is the switcher order. */\n\tlocales: readonly string[];\n\t/** The locale served without a URL prefix. */\n\tdefaultLocale: string;\n\t/** Cookie that persists the choice. Default `\"locale\"`. */\n\tcookieName?: string;\n\t/** Cookie max-age in seconds. Default one year. */\n\tcookieMaxAgeSeconds?: number;\n\t/** Prefix the default locale too (`/en/...`). Default `false`. */\n\tprefixDefaultLocale?: boolean;\n\t/**\n\t * Canonical (unprefixed) paths that have localized variants — exact matches.\n\t * Omit both this and `localizedPathPrefixes` to localize every path.\n\t */\n\tlocalizedPaths?: readonly string[];\n\t/** Canonical paths that have localized variants — prefix matches (e.g. `/blog/`). */\n\tlocalizedPathPrefixes?: readonly string[];\n}\n\nexport type LocaleSource = \"url\" | \"header\" | \"cookie\" | \"default\";\n\nexport interface ResolvedLocale {\n\tlocale: string;\n\t/** The canonical, unprefixed path for this request. */\n\tpath: string;\n\tsource: LocaleSource;\n}\n\nexport interface LocaleCookieOptions {\n\tpath: string;\n\tmaxAge: number;\n\tsameSite: \"lax\";\n}\n\nexport interface LocaleSwitchPlan {\n\t/** Localized URL to navigate to. */\n\thref: string;\n\t/** Cookie to persist the choice. */\n\tcookie: { name: string; value: string; options: LocaleCookieOptions };\n}\n\nexport function localeCookieName(config: LocaleConfig): string {\n\treturn config.cookieName ?? DEFAULT_LOCALE_COOKIE;\n}\n\nexport function localeCookieOptions(config: LocaleConfig): LocaleCookieOptions {\n\treturn {\n\t\tpath: \"/\",\n\t\tmaxAge: config.cookieMaxAgeSeconds ?? ONE_YEAR_SECONDS,\n\t\tsameSite: \"lax\",\n\t};\n}\n\nexport function isLocale(\n\tconfig: LocaleConfig,\n\tvalue: unknown,\n): value is string {\n\treturn typeof value === \"string\" && config.locales.includes(value);\n}\n\nexport function isLocalizedPath(\n\tconfig: LocaleConfig,\n\tpathname: string,\n): boolean {\n\tconst hasRules =\n\t\t(config.localizedPaths?.length ?? 0) > 0 ||\n\t\t(config.localizedPathPrefixes?.length ?? 0) > 0;\n\tif (!hasRules) return true;\n\tif (config.localizedPaths?.includes(pathname)) return true;\n\treturn (\n\t\tconfig.localizedPathPrefixes?.some(\n\t\t\t(prefix) => pathname === prefix || pathname.startsWith(prefix),\n\t\t) ?? false\n\t);\n}\n\n/** The leading locale segment of a path, if it is one we know. */\nexport function localeFromPath(\n\tconfig: LocaleConfig,\n\tpathname: string,\n): string | null {\n\tconst segment = pathname.split(\"/\")[1];\n\treturn segment && isLocale(config, segment) ? segment : null;\n}\n\n/** Strip a leading locale segment, returning the canonical path. */\nexport function stripLocale(\n\tconfig: LocaleConfig,\n\tpathname: string,\n): { locale: string | null; path: string } {\n\tconst locale = localeFromPath(config, pathname);\n\tif (!locale) return { locale: null, path: pathname || \"/\" };\n\tconst rest = pathname.slice(locale.length + 1);\n\treturn { locale, path: rest || \"/\" };\n}\n\n/**\n * Prefix a canonical path for a locale. The default locale stays bare unless\n * `prefixDefaultLocale` is set.\n */\nexport function localizePath(\n\tconfig: LocaleConfig,\n\tpathname: string,\n\tlocale: string,\n): string {\n\tif (locale === config.defaultLocale && !config.prefixDefaultLocale) {\n\t\treturn pathname;\n\t}\n\treturn pathname === \"/\" ? `/${locale}` : `/${locale}${pathname}`;\n}\n\n/** The URL to navigate to when switching `pathname` to `locale`. */\nexport function switchHref(\n\tconfig: LocaleConfig,\n\tpathname: string,\n\tlocale: string,\n): string {\n\tconst { path } = stripLocale(config, pathname);\n\treturn localizePath(config, path, locale);\n}\n\n/**\n * Resolve the active locale for a request. Precedence: URL prefix → forced\n * header → cookie → default. `path` is always the canonical (unprefixed) path.\n */\nexport function resolveLocale(\n\tconfig: LocaleConfig,\n\tinput: { pathname: string; header?: string | null; cookie?: string | null },\n): ResolvedLocale {\n\tconst fromPath = localeFromPath(config, input.pathname);\n\tif (fromPath) {\n\t\treturn {\n\t\t\tlocale: fromPath,\n\t\t\tpath: stripLocale(config, input.pathname).path,\n\t\t\tsource: \"url\",\n\t\t};\n\t}\n\tif (isLocale(config, input.header)) {\n\t\treturn { locale: input.header, path: input.pathname, source: \"header\" };\n\t}\n\tif (isLocale(config, input.cookie)) {\n\t\treturn { locale: input.cookie, path: input.pathname, source: \"cookie\" };\n\t}\n\treturn {\n\t\tlocale: config.defaultLocale,\n\t\tpath: input.pathname,\n\t\tsource: \"default\",\n\t};\n}\n\n/** The `Set-Cookie` value that persists `locale`. */\nexport function serializeLocaleCookie(\n\tconfig: LocaleConfig,\n\tlocale: string,\n): string {\n\tconst options = localeCookieOptions(config);\n\treturn `${localeCookieName(config)}=${encodeURIComponent(locale)}; Path=${\n\t\toptions.path\n\t}; Max-Age=${options.maxAge}; SameSite=${capitalize(options.sameSite)}`;\n}\n\n/** Read a single cookie value from a `Cookie:` header. */\nexport function parseCookie(\n\theader: string | null | undefined,\n\tname: string,\n): string | null {\n\tif (!header) return null;\n\tfor (const part of header.split(\";\")) {\n\t\tconst [key, ...rest] = part.trim().split(\"=\");\n\t\tif (key === name) return decodeURIComponent(rest.join(\"=\"));\n\t}\n\treturn null;\n}\n\n/**\n * Everything a client needs to switch locale: set `cookie`, navigate to `href`.\n * Returns `null` when the current path has no localized variant (legal pages,\n * API routes, …), so the switcher can no-op instead of linking to a 404.\n */\nexport function planSwitch(\n\tconfig: LocaleConfig,\n\tpathname: string,\n\tlocale: string,\n): LocaleSwitchPlan | null {\n\tconst { path } = stripLocale(config, pathname);\n\tif (!isLocalizedPath(config, path)) return null;\n\treturn {\n\t\thref: localizePath(config, path, locale),\n\t\tcookie: {\n\t\t\tname: localeCookieName(config),\n\t\t\tvalue: locale,\n\t\t\toptions: localeCookieOptions(config),\n\t\t},\n\t};\n}\n\nfunction capitalize(value: string): string {\n\treturn value.charAt(0).toUpperCase() + value.slice(1);\n}\n","/**\n * Astro adapter.\n *\n * Astro's middleware runs on every request, so it's the natural place to\n * resolve the locale, force it onto `Astro.locals.locale`, rewrite\n * locale-prefixed URLs to their canonical path, and persist the cookie.\n *\n * ```ts\n * // src/middleware.ts\n * import { defineMiddleware } from \"astro:middleware\";\n * import { createAstroLocaleMiddleware } from \"rosetta-i18n/astro\";\n * import { localeConfig } from \"../i18n/config\";\n *\n * export const onRequest = createAstroLocaleMiddleware(localeConfig);\n * ```\n *\n * For a client switch, render the React `<LocaleSwitcher>` as an island and\n * wrap it in `<LocaleProvider locale={Astro.locals.locale} config={localeConfig}>`.\n * Types are structural, so this module never depends on `astro`.\n */\n\nimport {\n\tisLocale,\n\tLOCALE_HEADER,\n\ttype LocaleConfig,\n\tlocaleCookieName,\n\tparseCookie,\n\tresolveLocale,\n\tserializeLocaleCookie,\n} from \"./core\";\n\nexport interface AstroLocaleContext {\n\trequest: Request;\n\tlocals: Record<string, unknown>;\n\t/** Astro's `context.rewrite()`. */\n\trewrite: (url: string | URL) => Promise<Response>;\n}\n\nexport type AstroMiddlewareNext = (rewrite?: URL) => Promise<Response>;\n\nexport function createAstroLocaleMiddleware(config: LocaleConfig) {\n\treturn async function onRequest(\n\t\tcontext: AstroLocaleContext,\n\t\tnext: AstroMiddlewareNext,\n\t): Promise<Response> {\n\t\tconst url = new URL(context.request.url);\n\t\tconst resolved = resolveLocale(config, {\n\t\t\tpathname: url.pathname,\n\t\t\theader: context.request.headers.get(LOCALE_HEADER),\n\t\t\tcookie: parseCookie(\n\t\t\t\tcontext.request.headers.get(\"cookie\"),\n\t\t\t\tlocaleCookieName(config),\n\t\t\t),\n\t\t});\n\n\t\tcontext.locals.locale = resolved.locale;\n\n\t\tif (resolved.source === \"url\") {\n\t\t\tconst target = new URL(url);\n\t\t\ttarget.pathname = resolved.path;\n\t\t\tconst response = await context.rewrite(target);\n\t\t\tresponse.headers.set(LOCALE_HEADER, resolved.locale);\n\t\t\tresponse.headers.append(\n\t\t\t\t\"Set-Cookie\",\n\t\t\t\tserializeLocaleCookie(config, resolved.locale),\n\t\t\t);\n\t\t\treturn response;\n\t\t}\n\n\t\tconst response = await next();\n\t\t// Persist only when the URL can't decide it (bare path) and the cookie\n\t\t// disagrees — never on a rewrite, where the URL is authoritative.\n\t\tif (resolved.source === \"cookie\" || resolved.source === \"default\") {\n\t\t\tresponse.headers.append(\n\t\t\t\t\"Set-Cookie\",\n\t\t\t\tserializeLocaleCookie(config, resolved.locale),\n\t\t\t);\n\t\t}\n\t\treturn response;\n\t};\n}\n\n/** Read the locale the middleware stored on `Astro.locals`. */\nexport function getAstroLocale(\n\tcontext: { locals: Record<string, unknown> },\n\tconfig: LocaleConfig,\n): string {\n\tconst locale = context.locals.locale;\n\treturn isLocale(config, locale) ? locale : config.defaultLocale;\n}\n"],"names":["response"],"mappings":"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"}