@tuwaio/docs-ui 0.5.6 → 0.6.0
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 +91 -3
- package/bin/i18n-check.js +19 -0
- package/dist/components/DocsLayout.cjs +82 -0
- package/dist/components/DocsLayout.d.cts +50 -0
- package/dist/components/DocsLayout.d.ts +50 -0
- package/dist/components/DocsLayout.js +58 -0
- package/dist/components/LocalizedPage.cjs +42 -0
- package/dist/components/LocalizedPage.d.cts +37 -0
- package/dist/components/LocalizedPage.d.ts +37 -0
- package/dist/components/LocalizedPage.js +18 -0
- package/dist/components/Logo.cjs +2 -2
- package/dist/components/Logo.d.cts +3 -0
- package/dist/components/Logo.d.ts +3 -0
- package/dist/components/Logo.js +2 -2
- package/dist/components/NavBar.cjs +4 -3
- package/dist/components/NavBar.d.cts +7 -2
- package/dist/components/NavBar.d.ts +7 -2
- package/dist/components/NavBar.js +4 -3
- package/dist/docsI18n.cjs +33 -0
- package/dist/docsI18n.d.cts +16 -0
- package/dist/docsI18n.d.ts +16 -0
- package/dist/docsI18n.js +8 -0
- package/dist/docsLabels.cjs +140 -0
- package/dist/docsLabels.d.cts +72 -0
- package/dist/docsLabels.d.ts +72 -0
- package/dist/docsLabels.js +116 -0
- package/dist/docsPage.cjs +70 -0
- package/dist/docsPage.d.cts +56 -0
- package/dist/docsPage.d.ts +56 -0
- package/dist/docsPage.js +46 -0
- package/dist/i18n.d.cts +1 -1
- package/dist/i18n.d.ts +1 -1
- package/dist/index.cjs +4 -0
- package/dist/index.css +26 -2
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/nextra.cjs +26 -0
- package/dist/nextra.d.cts +10 -0
- package/dist/nextra.d.ts +10 -0
- package/dist/nextra.js +3 -0
- package/dist/pageMap.cjs +55 -0
- package/dist/pageMap.d.cts +20 -0
- package/dist/pageMap.d.ts +20 -0
- package/dist/pageMap.js +31 -0
- package/dist/translationCheck.cjs +186 -0
- package/dist/translationCheck.d.cts +89 -0
- package/dist/translationCheck.d.ts +89 -0
- package/dist/translationCheck.js +157 -0
- package/package.json +17 -6
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { Metadata } from 'next';
|
|
3
|
+
import { EvaluateResult } from 'nextra';
|
|
4
|
+
import { TuwaLocale } from './locale.js';
|
|
5
|
+
|
|
6
|
+
/** A translated language of a page: a function that imports its MDX file (`() => import('./uk/index.mdx')`). */
|
|
7
|
+
type LocalizedPageLoaders = Partial<Record<Exclude<TuwaLocale, 'en'>, () => Promise<EvaluateResult>>>;
|
|
8
|
+
/** Props of the `[[...mdxPath]]` page of a documentation site. */
|
|
9
|
+
interface DocsPageProps {
|
|
10
|
+
/** The route segments after the site root. */
|
|
11
|
+
params: Promise<{
|
|
12
|
+
mdxPath?: string[];
|
|
13
|
+
}>;
|
|
14
|
+
/** Query parameters of the request. */
|
|
15
|
+
searchParams?: Promise<Record<string, string | string[] | undefined>>;
|
|
16
|
+
}
|
|
17
|
+
/** Options of {@link createDocsPage}. */
|
|
18
|
+
interface CreateDocsPageOptions {
|
|
19
|
+
/**
|
|
20
|
+
* The translated pages of the site by route (`/` for the Introduction, `/packages` for the packages overview).
|
|
21
|
+
* Pages that are not listed (package READMEs, the generated reference) stay in English in every language.
|
|
22
|
+
*/
|
|
23
|
+
localizedPages?: Readonly<Record<string, LocalizedPageLoaders>>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Creates the `[[...mdxPath]]` route of a TUWA documentation site (the pages of `src/content`): the static params, the
|
|
27
|
+
* metadata of each page and the page component, which renders the English page and its translations into a
|
|
28
|
+
* `LocalizedPage`. Re-export them from `src/app/[[...mdxPath]]/page.tsx`; the layout must be `DocsLayout`
|
|
29
|
+
* (`@tuwaio/docs-ui/nextra`). Side effects: imports the MDX files of the requested page and of its translations; the
|
|
30
|
+
* metadata stays English.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```tsx
|
|
34
|
+
* import { createDocsPage } from '@tuwaio/docs-ui/nextra/page';
|
|
35
|
+
*
|
|
36
|
+
* import { localizedPages } from '@/i18n/pages';
|
|
37
|
+
*
|
|
38
|
+
* const { generateStaticParams, generateMetadata, Page } = createDocsPage({ localizedPages });
|
|
39
|
+
*
|
|
40
|
+
* export { generateMetadata, generateStaticParams };
|
|
41
|
+
* export default Page;
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* @param options - See {@link CreateDocsPageOptions}.
|
|
45
|
+
* @returns `generateStaticParams`, `generateMetadata` and `Page`.
|
|
46
|
+
*/
|
|
47
|
+
declare function createDocsPage({ localizedPages }?: CreateDocsPageOptions): {
|
|
48
|
+
generateStaticParams: () => Promise<{
|
|
49
|
+
[x: string]: string | string[];
|
|
50
|
+
}[]>;
|
|
51
|
+
generateMetadata: (props: DocsPageProps) => Promise<Metadata>;
|
|
52
|
+
Page: (props: DocsPageProps) => Promise<react.JSX.Element>;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export { createDocsPage };
|
|
56
|
+
export type { CreateDocsPageOptions, DocsPageProps, LocalizedPageLoaders };
|
package/dist/docsPage.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { notFound } from "next/navigation";
|
|
3
|
+
import { generateStaticParamsFor, importPage } from "nextra/pages";
|
|
4
|
+
import { LocalizedPage } from "./components/LocalizedPage";
|
|
5
|
+
function isAssetOrInternal(mdxPath) {
|
|
6
|
+
if (!mdxPath || mdxPath.length === 0) return false;
|
|
7
|
+
if (mdxPath[0] === "_next") return true;
|
|
8
|
+
return Boolean(mdxPath[mdxPath.length - 1]?.includes("."));
|
|
9
|
+
}
|
|
10
|
+
const routeOf = (mdxPath) => `/${(mdxPath ?? []).join("/")}`;
|
|
11
|
+
function variantOf(module, props, params) {
|
|
12
|
+
const { default: MDXContent, toc, metadata, sourceCode } = module;
|
|
13
|
+
return { content: /* @__PURE__ */ jsx(MDXContent, { ...props, params }), toc, metadata, sourceCode };
|
|
14
|
+
}
|
|
15
|
+
function createDocsPage({ localizedPages = {} } = {}) {
|
|
16
|
+
const generateStaticParams = generateStaticParamsFor("mdxPath");
|
|
17
|
+
async function generateMetadata(props) {
|
|
18
|
+
const { mdxPath } = await props.params;
|
|
19
|
+
if (isAssetOrInternal(mdxPath)) return {};
|
|
20
|
+
try {
|
|
21
|
+
const { metadata } = await importPage(mdxPath);
|
|
22
|
+
return metadata;
|
|
23
|
+
} catch {
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
async function Page(props) {
|
|
28
|
+
const params = await props.params;
|
|
29
|
+
if (isAssetOrInternal(params.mdxPath)) notFound();
|
|
30
|
+
let english;
|
|
31
|
+
try {
|
|
32
|
+
english = await importPage(params.mdxPath);
|
|
33
|
+
} catch {
|
|
34
|
+
notFound();
|
|
35
|
+
}
|
|
36
|
+
const loaders = Object.entries(localizedPages[routeOf(params.mdxPath)] ?? {});
|
|
37
|
+
const translations = await Promise.all(
|
|
38
|
+
loaders.map(async ([locale, load]) => [locale, variantOf(await load(), props, params)])
|
|
39
|
+
);
|
|
40
|
+
return /* @__PURE__ */ jsx(LocalizedPage, { variants: { ...Object.fromEntries(translations), en: variantOf(english, props, params) } });
|
|
41
|
+
}
|
|
42
|
+
return { generateStaticParams, generateMetadata, Page };
|
|
43
|
+
}
|
|
44
|
+
export {
|
|
45
|
+
createDocsPage
|
|
46
|
+
};
|
package/dist/i18n.d.cts
CHANGED
package/dist/i18n.d.ts
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -42,8 +42,10 @@ __reExport(index_exports, require("./components/Orb"), module.exports);
|
|
|
42
42
|
__reExport(index_exports, require("./components/PolicyDocument"), module.exports);
|
|
43
43
|
var import_StarryBackground = __toESM(require("./components/StarryBackground"), 1);
|
|
44
44
|
__reExport(index_exports, require("./components/ThemeSwitcher"), module.exports);
|
|
45
|
+
__reExport(index_exports, require("./docsLabels"), module.exports);
|
|
45
46
|
__reExport(index_exports, require("./i18n"), module.exports);
|
|
46
47
|
__reExport(index_exports, require("./locale"), module.exports);
|
|
48
|
+
__reExport(index_exports, require("./pageMap"), module.exports);
|
|
47
49
|
__reExport(index_exports, require("./policies"), module.exports);
|
|
48
50
|
__reExport(index_exports, require("./preferences"), module.exports);
|
|
49
51
|
__reExport(index_exports, require("./utils"), module.exports);
|
|
@@ -58,8 +60,10 @@ __reExport(index_exports, require("./utils"), module.exports);
|
|
|
58
60
|
...require("./components/Orb"),
|
|
59
61
|
...require("./components/PolicyDocument"),
|
|
60
62
|
...require("./components/ThemeSwitcher"),
|
|
63
|
+
...require("./docsLabels"),
|
|
61
64
|
...require("./i18n"),
|
|
62
65
|
...require("./locale"),
|
|
66
|
+
...require("./pageMap"),
|
|
63
67
|
...require("./policies"),
|
|
64
68
|
...require("./preferences"),
|
|
65
69
|
...require("./utils")
|
package/dist/index.css
CHANGED
|
@@ -750,6 +750,9 @@ html {
|
|
|
750
750
|
.tuwadocs\:z-50 {
|
|
751
751
|
z-index: 50;
|
|
752
752
|
}
|
|
753
|
+
.tuwadocs\:clear-both {
|
|
754
|
+
clear: both;
|
|
755
|
+
}
|
|
753
756
|
.tuwadocs\:mt-1\.5 {
|
|
754
757
|
margin-top: calc(var(--tuwadocs-spacing) * 1.5);
|
|
755
758
|
}
|
|
@@ -849,6 +852,9 @@ html {
|
|
|
849
852
|
.tuwadocs\:w-full {
|
|
850
853
|
width: 100%;
|
|
851
854
|
}
|
|
855
|
+
.tuwadocs\:max-w-none {
|
|
856
|
+
max-width: none;
|
|
857
|
+
}
|
|
852
858
|
.tuwadocs\:min-w-0 {
|
|
853
859
|
min-width: 0px;
|
|
854
860
|
}
|
|
@@ -1164,6 +1170,9 @@ html {
|
|
|
1164
1170
|
--tw-tracking: var(--tuwadocs-tracking-widest);
|
|
1165
1171
|
letter-spacing: var(--tuwadocs-tracking-widest);
|
|
1166
1172
|
}
|
|
1173
|
+
.tuwadocs\:whitespace-nowrap {
|
|
1174
|
+
white-space: nowrap;
|
|
1175
|
+
}
|
|
1167
1176
|
.tuwadocs\:text-\[\#0052ff\] {
|
|
1168
1177
|
color: #0052ff;
|
|
1169
1178
|
}
|
|
@@ -1379,6 +1388,16 @@ html {
|
|
|
1379
1388
|
}
|
|
1380
1389
|
}
|
|
1381
1390
|
@media (width >= 64rem) {
|
|
1391
|
+
.tuwadocs\:lg\:not-sr-only {
|
|
1392
|
+
position: static;
|
|
1393
|
+
width: auto;
|
|
1394
|
+
height: auto;
|
|
1395
|
+
padding: 0;
|
|
1396
|
+
margin: 0;
|
|
1397
|
+
overflow: visible;
|
|
1398
|
+
clip-path: none;
|
|
1399
|
+
white-space: normal;
|
|
1400
|
+
}
|
|
1382
1401
|
.tuwadocs\:lg\:sticky {
|
|
1383
1402
|
position: sticky;
|
|
1384
1403
|
}
|
|
@@ -1458,6 +1477,9 @@ html {
|
|
|
1458
1477
|
display: none;
|
|
1459
1478
|
}
|
|
1460
1479
|
}
|
|
1480
|
+
.tuwadocs\:\[\&\>\:first-child\]\:shrink-0 > :first-child {
|
|
1481
|
+
flex-shrink: 0;
|
|
1482
|
+
}
|
|
1461
1483
|
}
|
|
1462
1484
|
html {
|
|
1463
1485
|
font-size: 16px;
|
|
@@ -2008,11 +2030,13 @@ article .nextra-border a:hover {
|
|
|
2008
2030
|
article a[href^='http']:after {
|
|
2009
2031
|
filter: hue-rotate(210deg) saturate(1.5);
|
|
2010
2032
|
}
|
|
2011
|
-
@media (max-width:
|
|
2033
|
+
@media (max-width: 767.98px), (pointer: coarse) and (max-width: 1023.98px) {
|
|
2012
2034
|
.nextra-navbar {
|
|
2013
2035
|
backdrop-filter: none;
|
|
2014
|
-
background:
|
|
2036
|
+
background: rgb(var(--nextra-bg, 255 255 255));
|
|
2015
2037
|
}
|
|
2038
|
+
}
|
|
2039
|
+
@media (max-width: 768px) {
|
|
2016
2040
|
.tuwa-footer-link span {
|
|
2017
2041
|
display: none;
|
|
2018
2042
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -7,9 +7,12 @@ export { Orb, OrbProps, PackageType } from './components/Orb.cjs';
|
|
|
7
7
|
export { PolicyDocument, PolicyDocumentProps } from './components/PolicyDocument.cjs';
|
|
8
8
|
export { default as StarryBackground } from './components/StarryBackground.cjs';
|
|
9
9
|
export { ThemeSwitcher, ThemeSwitcherProps } from './components/ThemeSwitcher.cjs';
|
|
10
|
+
export { DocsLabels, docsLabels } from './docsLabels.cjs';
|
|
10
11
|
export { I18nContextValue, I18nKit, I18nProviderProps, LocaleState, LocaleStateOptions, LocaleVariant, createI18n, useLocaleState } from './i18n.cjs';
|
|
11
12
|
export { DEFAULT_LOCALE, LOCALE_STORAGE_KEY, LocaleSignals, TUWA_LOCALES, TuwaLocale, detectLocale, formatLabel, normalizeLocale, policyLanguageOf, resolveClientLocale } from './locale.cjs';
|
|
13
|
+
export { localizePageMap } from './pageMap.cjs';
|
|
12
14
|
export { POLICIES_INDEX_URL, POLICY_DOCUMENT_LABELS, POLICY_LANGUAGES, POLICY_LANGUAGE_NAMES, POLICY_SITE_URL, POLICY_SOURCE, Policy, PolicyContent, PolicyDocumentLabels, PolicyLanguage, PolicySection, PolicySlug, footerPolicies, formatPolicyDate, getPolicyFileUrl, getPolicyPageUrl, loadPolicy, policies, policyLanguageFrom, publishedPolicies } from './policies.cjs';
|
|
13
15
|
export { PREFERENCE_DAYS, PreferenceExpiry, expireStoredPreferences } from './preferences.cjs';
|
|
14
16
|
export { baseFooterLinks, baseNavLinks } from './utils.cjs';
|
|
15
17
|
import 'react';
|
|
18
|
+
import 'nextra';
|
package/dist/index.d.ts
CHANGED
|
@@ -7,9 +7,12 @@ export { Orb, OrbProps, PackageType } from './components/Orb.js';
|
|
|
7
7
|
export { PolicyDocument, PolicyDocumentProps } from './components/PolicyDocument.js';
|
|
8
8
|
export { default as StarryBackground } from './components/StarryBackground.js';
|
|
9
9
|
export { ThemeSwitcher, ThemeSwitcherProps } from './components/ThemeSwitcher.js';
|
|
10
|
+
export { DocsLabels, docsLabels } from './docsLabels.js';
|
|
10
11
|
export { I18nContextValue, I18nKit, I18nProviderProps, LocaleState, LocaleStateOptions, LocaleVariant, createI18n, useLocaleState } from './i18n.js';
|
|
11
12
|
export { DEFAULT_LOCALE, LOCALE_STORAGE_KEY, LocaleSignals, TUWA_LOCALES, TuwaLocale, detectLocale, formatLabel, normalizeLocale, policyLanguageOf, resolveClientLocale } from './locale.js';
|
|
13
|
+
export { localizePageMap } from './pageMap.js';
|
|
12
14
|
export { POLICIES_INDEX_URL, POLICY_DOCUMENT_LABELS, POLICY_LANGUAGES, POLICY_LANGUAGE_NAMES, POLICY_SITE_URL, POLICY_SOURCE, Policy, PolicyContent, PolicyDocumentLabels, PolicyLanguage, PolicySection, PolicySlug, footerPolicies, formatPolicyDate, getPolicyFileUrl, getPolicyPageUrl, loadPolicy, policies, policyLanguageFrom, publishedPolicies } from './policies.js';
|
|
13
15
|
export { PREFERENCE_DAYS, PreferenceExpiry, expireStoredPreferences } from './preferences.js';
|
|
14
16
|
export { baseFooterLinks, baseNavLinks } from './utils.js';
|
|
15
17
|
import 'react';
|
|
18
|
+
import 'nextra';
|
package/dist/index.js
CHANGED
|
@@ -7,8 +7,10 @@ export * from "./components/Orb";
|
|
|
7
7
|
export * from "./components/PolicyDocument";
|
|
8
8
|
import { default as default3 } from "./components/StarryBackground";
|
|
9
9
|
export * from "./components/ThemeSwitcher";
|
|
10
|
+
export * from "./docsLabels";
|
|
10
11
|
export * from "./i18n";
|
|
11
12
|
export * from "./locale";
|
|
13
|
+
export * from "./pageMap";
|
|
12
14
|
export * from "./policies";
|
|
13
15
|
export * from "./preferences";
|
|
14
16
|
export * from "./utils";
|
package/dist/nextra.cjs
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __copyProps = (to, from, except, desc) => {
|
|
7
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
8
|
+
for (let key of __getOwnPropNames(from))
|
|
9
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
10
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
11
|
+
}
|
|
12
|
+
return to;
|
|
13
|
+
};
|
|
14
|
+
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
15
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
16
|
+
var nextra_exports = {};
|
|
17
|
+
module.exports = __toCommonJS(nextra_exports);
|
|
18
|
+
__reExport(nextra_exports, require("./components/DocsLayout"), module.exports);
|
|
19
|
+
__reExport(nextra_exports, require("./components/LocalizedPage"), module.exports);
|
|
20
|
+
__reExport(nextra_exports, require("./docsI18n"), module.exports);
|
|
21
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
22
|
+
0 && (module.exports = {
|
|
23
|
+
...require("./components/DocsLayout"),
|
|
24
|
+
...require("./components/LocalizedPage"),
|
|
25
|
+
...require("./docsI18n")
|
|
26
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { DocsLayout, DocsLayoutProps } from './components/DocsLayout.cjs';
|
|
2
|
+
export { LocalizedPage, LocalizedPageProps, LocalizedPageVariant } from './components/LocalizedPage.cjs';
|
|
3
|
+
export { DocsI18nProvider, useDocsI18n } from './docsI18n.cjs';
|
|
4
|
+
import 'react';
|
|
5
|
+
import 'nextra';
|
|
6
|
+
import './locale.cjs';
|
|
7
|
+
import './components/NavBar.cjs';
|
|
8
|
+
import './docsLabels.cjs';
|
|
9
|
+
import './policies.cjs';
|
|
10
|
+
import './i18n.cjs';
|
package/dist/nextra.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { DocsLayout, DocsLayoutProps } from './components/DocsLayout.js';
|
|
2
|
+
export { LocalizedPage, LocalizedPageProps, LocalizedPageVariant } from './components/LocalizedPage.js';
|
|
3
|
+
export { DocsI18nProvider, useDocsI18n } from './docsI18n.js';
|
|
4
|
+
import 'react';
|
|
5
|
+
import 'nextra';
|
|
6
|
+
import './locale.js';
|
|
7
|
+
import './components/NavBar.js';
|
|
8
|
+
import './docsLabels.js';
|
|
9
|
+
import './policies.js';
|
|
10
|
+
import './i18n.js';
|
package/dist/nextra.js
ADDED
package/dist/pageMap.cjs
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var pageMap_exports = {};
|
|
20
|
+
__export(pageMap_exports, {
|
|
21
|
+
localizePageMap: () => localizePageMap
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(pageMap_exports);
|
|
24
|
+
function localizeMetaValue(value, titles) {
|
|
25
|
+
if (typeof value === "string") return titles[value] ?? value;
|
|
26
|
+
if (value && typeof value === "object" && typeof value.title === "string") {
|
|
27
|
+
const translated = titles[value.title];
|
|
28
|
+
return translated ? { ...value, title: translated } : value;
|
|
29
|
+
}
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
function localizePageMap(pageMap, titles) {
|
|
33
|
+
if (Object.keys(titles).length === 0) return pageMap;
|
|
34
|
+
return pageMap.map((item) => {
|
|
35
|
+
if ("data" in item) {
|
|
36
|
+
const data = Object.fromEntries(
|
|
37
|
+
Object.entries(item.data).map(([key, value]) => [key, localizeMetaValue(value, titles)])
|
|
38
|
+
);
|
|
39
|
+
return { ...item, data };
|
|
40
|
+
}
|
|
41
|
+
const copy = { ...item };
|
|
42
|
+
const title = item.title;
|
|
43
|
+
if (typeof title === "string" && titles[title]) copy.title = titles[title];
|
|
44
|
+
if ("frontMatter" in item && item.frontMatter && typeof item.frontMatter.title === "string") {
|
|
45
|
+
const title2 = titles[item.frontMatter.title];
|
|
46
|
+
if (title2) copy.frontMatter = { ...item.frontMatter, title: title2 };
|
|
47
|
+
}
|
|
48
|
+
if ("children" in item && Array.isArray(item.children)) copy.children = localizePageMap(item.children, titles);
|
|
49
|
+
return copy;
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
53
|
+
0 && (module.exports = {
|
|
54
|
+
localizePageMap
|
|
55
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { PageMapItem } from 'nextra';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Translates the titles of a Nextra page map: the sidebar, the top-level menu, the breadcrumbs and the previous / next
|
|
5
|
+
* links all read them. Titles are matched by their English text (the `_meta` titles, the `title` of each item and the
|
|
6
|
+
* front matter `title`), so a site lists each English title once, whatever the page; titles without a translation,
|
|
7
|
+
* such as package names, stay as they are. The page map is not modified: changed items are copied.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* localizePageMap(pageMap, { Introduction: 'Вступ', Packages: 'Пакети' });
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* @param pageMap - The page map of the site, the result of `getPageMap()` of `nextra/page-map`.
|
|
15
|
+
* @param titles - Translated titles by English title.
|
|
16
|
+
* @returns The page map with translated titles.
|
|
17
|
+
*/
|
|
18
|
+
declare function localizePageMap(pageMap: PageMapItem[], titles: Readonly<Record<string, string>>): PageMapItem[];
|
|
19
|
+
|
|
20
|
+
export { localizePageMap };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { PageMapItem } from 'nextra';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Translates the titles of a Nextra page map: the sidebar, the top-level menu, the breadcrumbs and the previous / next
|
|
5
|
+
* links all read them. Titles are matched by their English text (the `_meta` titles, the `title` of each item and the
|
|
6
|
+
* front matter `title`), so a site lists each English title once, whatever the page; titles without a translation,
|
|
7
|
+
* such as package names, stay as they are. The page map is not modified: changed items are copied.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* localizePageMap(pageMap, { Introduction: 'Вступ', Packages: 'Пакети' });
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* @param pageMap - The page map of the site, the result of `getPageMap()` of `nextra/page-map`.
|
|
15
|
+
* @param titles - Translated titles by English title.
|
|
16
|
+
* @returns The page map with translated titles.
|
|
17
|
+
*/
|
|
18
|
+
declare function localizePageMap(pageMap: PageMapItem[], titles: Readonly<Record<string, string>>): PageMapItem[];
|
|
19
|
+
|
|
20
|
+
export { localizePageMap };
|
package/dist/pageMap.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
function localizeMetaValue(value, titles) {
|
|
2
|
+
if (typeof value === "string") return titles[value] ?? value;
|
|
3
|
+
if (value && typeof value === "object" && typeof value.title === "string") {
|
|
4
|
+
const translated = titles[value.title];
|
|
5
|
+
return translated ? { ...value, title: translated } : value;
|
|
6
|
+
}
|
|
7
|
+
return value;
|
|
8
|
+
}
|
|
9
|
+
function localizePageMap(pageMap, titles) {
|
|
10
|
+
if (Object.keys(titles).length === 0) return pageMap;
|
|
11
|
+
return pageMap.map((item) => {
|
|
12
|
+
if ("data" in item) {
|
|
13
|
+
const data = Object.fromEntries(
|
|
14
|
+
Object.entries(item.data).map(([key, value]) => [key, localizeMetaValue(value, titles)])
|
|
15
|
+
);
|
|
16
|
+
return { ...item, data };
|
|
17
|
+
}
|
|
18
|
+
const copy = { ...item };
|
|
19
|
+
const title = item.title;
|
|
20
|
+
if (typeof title === "string" && titles[title]) copy.title = titles[title];
|
|
21
|
+
if ("frontMatter" in item && item.frontMatter && typeof item.frontMatter.title === "string") {
|
|
22
|
+
const title2 = titles[item.frontMatter.title];
|
|
23
|
+
if (title2) copy.frontMatter = { ...item.frontMatter, title: title2 };
|
|
24
|
+
}
|
|
25
|
+
if ("children" in item && Array.isArray(item.children)) copy.children = localizePageMap(item.children, titles);
|
|
26
|
+
return copy;
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
export {
|
|
30
|
+
localizePageMap
|
|
31
|
+
};
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var translationCheck_exports = {};
|
|
20
|
+
__export(translationCheck_exports, {
|
|
21
|
+
checkSiteTranslations: () => checkSiteTranslations,
|
|
22
|
+
checkTranslation: () => checkTranslation,
|
|
23
|
+
extractCodeBlocks: () => extractCodeBlocks,
|
|
24
|
+
extractHeadings: () => extractHeadings,
|
|
25
|
+
extractLinks: () => extractLinks,
|
|
26
|
+
loadNextraSlugger: () => loadNextraSlugger
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(translationCheck_exports);
|
|
29
|
+
var import_node_fs = require("node:fs");
|
|
30
|
+
var import_node_module = require("node:module");
|
|
31
|
+
var import_node_path = require("node:path");
|
|
32
|
+
var import_node_url = require("node:url");
|
|
33
|
+
var import_locale = require("./locale.js");
|
|
34
|
+
const FENCE_RE = /^ {0,3}(`{3,}|~{3,})/;
|
|
35
|
+
function splitFences(source) {
|
|
36
|
+
const blocks = [];
|
|
37
|
+
const prose = [];
|
|
38
|
+
let block = null;
|
|
39
|
+
let fence = "";
|
|
40
|
+
for (const line of source.split("\n")) {
|
|
41
|
+
if (block) {
|
|
42
|
+
block.push(line);
|
|
43
|
+
if (line.trim().startsWith(fence) && line.trim().replace(/[`~]/g, "") === "") {
|
|
44
|
+
blocks.push(block.join("\n"));
|
|
45
|
+
block = null;
|
|
46
|
+
}
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
const open = FENCE_RE.exec(line);
|
|
50
|
+
if (open) {
|
|
51
|
+
fence = open[1];
|
|
52
|
+
block = [line];
|
|
53
|
+
} else {
|
|
54
|
+
prose.push(line);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (block) blocks.push(block.join("\n"));
|
|
58
|
+
return { blocks, prose: prose.join("\n") };
|
|
59
|
+
}
|
|
60
|
+
function extractCodeBlocks(source) {
|
|
61
|
+
return splitFences(source).blocks;
|
|
62
|
+
}
|
|
63
|
+
function plainText(markdown) {
|
|
64
|
+
return markdown.replace(/!?\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/(\*\*|__)(.+?)\1/g, "$2").replace(/(\*|_)(.+?)\1/g, "$2").replace(/`([^`]*)`/g, "$1").trim();
|
|
65
|
+
}
|
|
66
|
+
function extractHeadings(source) {
|
|
67
|
+
const headings = [];
|
|
68
|
+
for (const line of splitFences(source).prose.split("\n")) {
|
|
69
|
+
const match = /^ {0,3}(#{1,6})\s+(.+?)\s*#*\s*$/.exec(line);
|
|
70
|
+
if (!match) continue;
|
|
71
|
+
const custom = /\s*\[#([^\]]+)\]\s*$/.exec(match[2]);
|
|
72
|
+
const text = plainText(custom ? match[2].slice(0, custom.index) : match[2]);
|
|
73
|
+
headings.push({ depth: match[1].length, text, ...custom && { customId: custom[1] } });
|
|
74
|
+
}
|
|
75
|
+
return headings;
|
|
76
|
+
}
|
|
77
|
+
function extractLinks(source) {
|
|
78
|
+
const prose = splitFences(source).prose.replace(/`[^`\n]*`/g, "");
|
|
79
|
+
const targets = [];
|
|
80
|
+
for (const match of prose.matchAll(/\]\(\s*<?([^)\s>]+)>?(?:\s+"[^"]*")?\s*\)|\b(?:href|src)=["']([^"']+)["']/g)) {
|
|
81
|
+
targets.push(match[1] ?? match[2]);
|
|
82
|
+
}
|
|
83
|
+
return targets;
|
|
84
|
+
}
|
|
85
|
+
function headingIds(headings, createSlugger) {
|
|
86
|
+
const slug = createSlugger();
|
|
87
|
+
return headings.filter((heading) => heading.depth > 1).map((heading) => slug(heading.customId ?? heading.text));
|
|
88
|
+
}
|
|
89
|
+
const sorted = (values) => [...values].sort();
|
|
90
|
+
function checkTranslation(english, translated, createSlugger) {
|
|
91
|
+
const issues = [];
|
|
92
|
+
const englishBlocks = extractCodeBlocks(english);
|
|
93
|
+
const translatedBlocks = extractCodeBlocks(translated);
|
|
94
|
+
if (englishBlocks.length !== translatedBlocks.length) {
|
|
95
|
+
issues.push(`has ${translatedBlocks.length} code blocks, the English page ${englishBlocks.length}`);
|
|
96
|
+
} else {
|
|
97
|
+
englishBlocks.forEach((block, index) => {
|
|
98
|
+
if (block !== translatedBlocks[index]) issues.push(`code block ${index + 1} differs from the English one`);
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
const englishHeadings = extractHeadings(english);
|
|
102
|
+
const translatedHeadings = extractHeadings(translated);
|
|
103
|
+
const levels = (headings) => headings.map((heading) => "#".repeat(heading.depth)).join(" ");
|
|
104
|
+
if (levels(englishHeadings) !== levels(translatedHeadings)) {
|
|
105
|
+
issues.push(`heading levels "${levels(translatedHeadings)}" differ from the English "${levels(englishHeadings)}"`);
|
|
106
|
+
} else if (createSlugger) {
|
|
107
|
+
const englishIds = headingIds(englishHeadings, createSlugger);
|
|
108
|
+
const translatedIds = headingIds(translatedHeadings, createSlugger);
|
|
109
|
+
englishIds.forEach((id, index) => {
|
|
110
|
+
if (id !== translatedIds[index]) {
|
|
111
|
+
issues.push(
|
|
112
|
+
`heading "${translatedHeadings.filter((h) => h.depth > 1)[index].text}" has the id "${translatedIds[index]}", the English one "${id}" (add [#${id}])`
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
const englishLinks = sorted(extractLinks(english));
|
|
118
|
+
const translatedLinks = sorted(extractLinks(translated));
|
|
119
|
+
const missing = englishLinks.filter((link) => !translatedLinks.includes(link));
|
|
120
|
+
const extra = translatedLinks.filter((link) => !englishLinks.includes(link));
|
|
121
|
+
if (missing.length || extra.length || englishLinks.length !== translatedLinks.length) {
|
|
122
|
+
issues.push(
|
|
123
|
+
`links differ from the English page (missing: ${missing.join(", ") || "none"}; not in English: ${extra.join(", ") || "none"}; ${translatedLinks.length} vs ${englishLinks.length})`
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
return issues;
|
|
127
|
+
}
|
|
128
|
+
function pageFiles(dir) {
|
|
129
|
+
if (!(0, import_node_fs.existsSync)(dir)) return [];
|
|
130
|
+
return (0, import_node_fs.readdirSync)(dir, { recursive: true, encoding: "utf8" }).filter((file) => [".md", ".mdx"].includes((0, import_node_path.extname)(file))).map((file) => file.split(import_node_path.sep).join("/")).sort();
|
|
131
|
+
}
|
|
132
|
+
const withoutExtension = (file) => file.slice(0, -(0, import_node_path.extname)(file).length);
|
|
133
|
+
async function loadNextraSlugger(cwd) {
|
|
134
|
+
try {
|
|
135
|
+
const nextra = (0, import_node_module.createRequire)((0, import_node_path.join)(cwd, "package.json")).resolve("nextra/package.json");
|
|
136
|
+
const sluggerPath = (0, import_node_module.createRequire)(nextra).resolve("github-slugger");
|
|
137
|
+
const { default: GithubSlugger } = await import((0, import_node_url.pathToFileURL)(sluggerPath).href);
|
|
138
|
+
return () => {
|
|
139
|
+
const slugger = new GithubSlugger();
|
|
140
|
+
return (text) => slugger.slug(text);
|
|
141
|
+
};
|
|
142
|
+
} catch {
|
|
143
|
+
return void 0;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
async function checkSiteTranslations(options = {}) {
|
|
147
|
+
const { cwd = process.cwd(), contentDir = "src/content", i18nDir = "src/i18n" } = options;
|
|
148
|
+
if (!(0, import_node_fs.existsSync)((0, import_node_path.join)(cwd, contentDir))) {
|
|
149
|
+
throw new Error(`No ${contentDir} in ${cwd}: run the check from the docs app of the site`);
|
|
150
|
+
}
|
|
151
|
+
const createSlugger = options.createSlugger ?? await loadNextraSlugger(cwd);
|
|
152
|
+
const locales = import_locale.TUWA_LOCALES.filter((locale) => locale !== import_locale.DEFAULT_LOCALE);
|
|
153
|
+
const filesByLocale = new Map(locales.map((locale) => [locale, pageFiles((0, import_node_path.join)(cwd, i18nDir, locale))]));
|
|
154
|
+
const pages = [...new Set([...filesByLocale.values()].flat().map(withoutExtension))].sort();
|
|
155
|
+
const issues = [];
|
|
156
|
+
let files = 0;
|
|
157
|
+
for (const page of pages) {
|
|
158
|
+
const englishFile = [".mdx", ".md"].map((ext) => (0, import_node_path.join)(cwd, contentDir, page + ext)).find(import_node_fs.existsSync);
|
|
159
|
+
for (const locale of locales) {
|
|
160
|
+
const translatedFile = filesByLocale.get(locale)?.find((file) => withoutExtension(file) === page);
|
|
161
|
+
const shown = (0, import_node_path.join)(i18nDir, locale, translatedFile ?? `${page}.mdx`).split(import_node_path.sep).join("/");
|
|
162
|
+
if (!translatedFile) {
|
|
163
|
+
issues.push({ file: shown, message: "is missing: every translated page exists in every language" });
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
files += 1;
|
|
167
|
+
if (!englishFile) {
|
|
168
|
+
issues.push({ file: shown, message: `has no English page ${(0, import_node_path.join)(contentDir, page)}.mdx` });
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
const english = (0, import_node_fs.readFileSync)(englishFile, "utf8");
|
|
172
|
+
const translated = (0, import_node_fs.readFileSync)((0, import_node_path.join)(cwd, i18nDir, locale, translatedFile), "utf8");
|
|
173
|
+
for (const message of checkTranslation(english, translated, createSlugger)) issues.push({ file: shown, message });
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return { files, issues };
|
|
177
|
+
}
|
|
178
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
179
|
+
0 && (module.exports = {
|
|
180
|
+
checkSiteTranslations,
|
|
181
|
+
checkTranslation,
|
|
182
|
+
extractCodeBlocks,
|
|
183
|
+
extractHeadings,
|
|
184
|
+
extractLinks,
|
|
185
|
+
loadNextraSlugger
|
|
186
|
+
});
|