docpensieve 0.5.0-beta.1 → 0.5.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.
- package/bin/docpensieve.js +1 -0
- package/package.json +5 -5
- package/src/commands/init.js +199 -18
- package/starter/01-guide/01-installation.md +1 -0
- package/starter/01-guide/10-languages.md +13 -0
- package/starter/03-reference/01-cli.md +1 -0
- package/starter/03-reference/05-api.md +36 -3
- package/starter/05-whats-new.md +19 -3
- package/types/commands/init.d.ts +10 -5
package/bin/docpensieve.js
CHANGED
|
@@ -34,6 +34,7 @@ program
|
|
|
34
34
|
.option('-t, --theme <framework>', 'tailwind | custom')
|
|
35
35
|
.option('-u, --site-url <url>', 'public URL of the site')
|
|
36
36
|
.option('--version-name <version>', 'first version, e.g. 1.0')
|
|
37
|
+
.option('--translation <code>', 'code of a second language, e.g. fr')
|
|
37
38
|
.option('-y, --yes', 'accept the defaults without a dialogue')
|
|
38
39
|
.option('-f, --force', 'overwrite an existing configuration')
|
|
39
40
|
.option('--minimal', "leave DocPensieve's documentation out of the new site")
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "docpensieve",
|
|
3
|
-
"version": "0.5.0-beta.
|
|
3
|
+
"version": "0.5.0-beta.2",
|
|
4
4
|
"description": "DocPensieve command-line interface (init, build, check, dev, serve)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -24,10 +24,10 @@
|
|
|
24
24
|
"types"
|
|
25
25
|
],
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@docpensieve/components": "0.5.0-beta.
|
|
28
|
-
"@docpensieve/core": "0.5.0-beta.
|
|
29
|
-
"@docpensieve/shared": "0.5.0-beta.
|
|
30
|
-
"@docpensieve/theme": "0.5.0-beta.
|
|
27
|
+
"@docpensieve/components": "0.5.0-beta.2",
|
|
28
|
+
"@docpensieve/core": "0.5.0-beta.2",
|
|
29
|
+
"@docpensieve/shared": "0.5.0-beta.2",
|
|
30
|
+
"@docpensieve/theme": "0.5.0-beta.2",
|
|
31
31
|
"chalk": "^6.0.0",
|
|
32
32
|
"chokidar": "^5.0.0",
|
|
33
33
|
"commander": "^15.0.0"
|
package/src/commands/init.js
CHANGED
|
@@ -18,6 +18,8 @@ import {
|
|
|
18
18
|
DocPensieveError,
|
|
19
19
|
THEME_FOLDER,
|
|
20
20
|
THEME_FRAMEWORKS,
|
|
21
|
+
isLanguageCode,
|
|
22
|
+
languageName,
|
|
21
23
|
} from '@docpensieve/shared';
|
|
22
24
|
|
|
23
25
|
/**
|
|
@@ -30,7 +32,16 @@ const FRAMEWORK_LABELS = {
|
|
|
30
32
|
};
|
|
31
33
|
|
|
32
34
|
/** Answers used when there is no dialogue. */
|
|
33
|
-
const DEFAULTS = {
|
|
35
|
+
const DEFAULTS = {
|
|
36
|
+
name: 'My documentation',
|
|
37
|
+
siteUrl: '',
|
|
38
|
+
theme: 'tailwind',
|
|
39
|
+
version: '1.0',
|
|
40
|
+
translation: '',
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/** Second language offered first, being the one whose wording ships too. */
|
|
44
|
+
const DEFAULT_TRANSLATION = 'fr';
|
|
34
45
|
|
|
35
46
|
/**
|
|
36
47
|
* Folder of the installed DocPensieve documentation, inside the version folder.
|
|
@@ -125,15 +136,43 @@ async function askDocumentation(rl) {
|
|
|
125
136
|
}
|
|
126
137
|
}
|
|
127
138
|
|
|
139
|
+
/**
|
|
140
|
+
* Asks whether the site will carry a second language, and which one.
|
|
141
|
+
*
|
|
142
|
+
* Asked rather than left to the configuration: a translation changes the
|
|
143
|
+
* shape of the project — a folder per language beside the pages — and that is
|
|
144
|
+
* cheaper to set up at the start than to retrofit.
|
|
145
|
+
*
|
|
146
|
+
* @param {import('node:readline/promises').Interface} rl
|
|
147
|
+
* @returns {Promise<string>} Language code, or `''` for a single language.
|
|
148
|
+
*/
|
|
149
|
+
async function askTranslation(rl) {
|
|
150
|
+
for (;;) {
|
|
151
|
+
const answer = (await rl.question('Will the site be in several languages? [y/N]: '))
|
|
152
|
+
.trim()
|
|
153
|
+
.toLowerCase();
|
|
154
|
+
if (answer === '' || answer === 'n' || answer === 'no') return '';
|
|
155
|
+
if (answer === 'y' || answer === 'yes') break;
|
|
156
|
+
console.log('Answer not understood. Expected: y or n.');
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
for (;;) {
|
|
160
|
+
const code = (await ask(rl, 'Code of the second language', DEFAULT_TRANSLATION)).trim();
|
|
161
|
+
if (isLanguageCode(code)) return code;
|
|
162
|
+
console.log(`"${code}" does not name a language. Expected a code: fr, de, pt-BR, zh-Hans.`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
128
166
|
/**
|
|
129
167
|
* Gathers the answers, through a dialogue or from the options.
|
|
130
168
|
*
|
|
131
169
|
* @param {{
|
|
132
170
|
* name?: string, theme?: string, siteUrl?: string, version?: string,
|
|
133
|
-
* yes?: boolean, minimal?: boolean,
|
|
171
|
+
* translation?: string, yes?: boolean, minimal?: boolean,
|
|
134
172
|
* }} options
|
|
135
173
|
* @returns {Promise<{
|
|
136
174
|
* name: string, theme: string, siteUrl: string, version: string, docs: boolean,
|
|
175
|
+
* translation: string,
|
|
137
176
|
* }>}
|
|
138
177
|
*/
|
|
139
178
|
async function collect(options) {
|
|
@@ -142,6 +181,7 @@ async function collect(options) {
|
|
|
142
181
|
siteUrl: options.siteUrl ?? DEFAULTS.siteUrl,
|
|
143
182
|
theme: options.theme ?? DEFAULTS.theme,
|
|
144
183
|
version: options.version ?? DEFAULTS.version,
|
|
184
|
+
translation: options.translation ?? DEFAULTS.translation,
|
|
145
185
|
docs: !options.minimal,
|
|
146
186
|
};
|
|
147
187
|
|
|
@@ -154,7 +194,7 @@ async function collect(options) {
|
|
|
154
194
|
if (!process.stdin.isTTY) {
|
|
155
195
|
console.log('No interactive terminal: no questions asked, the options and defaults apply.');
|
|
156
196
|
console.log(
|
|
157
|
-
'To choose, pass --name, --site-url, --theme, --version-name or --minimal; --yes silences this notice.',
|
|
197
|
+
'To choose, pass --name, --site-url, --theme, --version-name, --translation or --minimal; --yes silences this notice.',
|
|
158
198
|
);
|
|
159
199
|
return fromOptions;
|
|
160
200
|
}
|
|
@@ -164,9 +204,10 @@ async function collect(options) {
|
|
|
164
204
|
const name = await ask(rl, 'Project name', fromOptions.name);
|
|
165
205
|
const siteUrl = await ask(rl, 'Public URL of the site (optional)', fromOptions.siteUrl);
|
|
166
206
|
const version = await ask(rl, 'First version', fromOptions.version);
|
|
207
|
+
const translation = options.translation ?? (await askTranslation(rl));
|
|
167
208
|
const theme = options.theme ?? (await askFramework(rl));
|
|
168
209
|
const docs = options.minimal ? false : await askDocumentation(rl);
|
|
169
|
-
return { name, siteUrl, version, theme, docs };
|
|
210
|
+
return { name, siteUrl, version, theme, docs, translation };
|
|
170
211
|
} finally {
|
|
171
212
|
rl.close();
|
|
172
213
|
}
|
|
@@ -183,6 +224,18 @@ async function collect(options) {
|
|
|
183
224
|
*/
|
|
184
225
|
const quote = (value) => `'${String(value).replace(/\\/g, '\\\\').replace(/'/g, "\\'")}'`;
|
|
185
226
|
|
|
227
|
+
/**
|
|
228
|
+
* Writes a language code as a property name.
|
|
229
|
+
*
|
|
230
|
+
* `fr` stands on its own; `pt-BR` and `zh-Hans` carry a hyphen, which is a
|
|
231
|
+
* minus sign to JavaScript — unquoted, the generated configuration would not
|
|
232
|
+
* parse.
|
|
233
|
+
*
|
|
234
|
+
* @param {string} code
|
|
235
|
+
* @returns {string}
|
|
236
|
+
*/
|
|
237
|
+
const key = (code) => (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(code) ? code : quote(code));
|
|
238
|
+
|
|
186
239
|
/**
|
|
187
240
|
* Renders `docpensieve.config.mjs`.
|
|
188
241
|
*
|
|
@@ -190,10 +243,13 @@ const quote = (value) => `'${String(value).replace(/\\/g, '\\\\').replace(/'/g,
|
|
|
190
243
|
* or commented out with an example — so that the first file a user opens also
|
|
191
244
|
* tells them everything they can change.
|
|
192
245
|
*
|
|
193
|
-
* @param {{
|
|
246
|
+
* @param {{
|
|
247
|
+
* name: string, theme: string, siteUrl: string, version: string,
|
|
248
|
+
* translation?: string,
|
|
249
|
+
* }} answers
|
|
194
250
|
* @returns {string} Contents of `docpensieve.config.mjs`.
|
|
195
251
|
*/
|
|
196
|
-
function renderConfig({ name, theme, siteUrl, version }) {
|
|
252
|
+
function renderConfig({ name, theme, siteUrl, version, translation = '' }) {
|
|
197
253
|
const slug = versionSlug(version);
|
|
198
254
|
// A type annotation rather than an `import`: `defineConfig` transforms
|
|
199
255
|
// nothing, it is only there for autocompletion. Actually importing it would
|
|
@@ -233,9 +289,14 @@ function renderConfig({ name, theme, siteUrl, version }) {
|
|
|
233
289
|
` name: ${quote(version)}, // label in the version switcher`,
|
|
234
290
|
` folder: ${quote(`docs/${slug}`)},`,
|
|
235
291
|
' current: true,',
|
|
236
|
-
|
|
292
|
+
` // Pages of this version in another language, served under /${translation || 'fr'}/.`,
|
|
237
293
|
' // Your own language stays where it is, and keeps its addresses.',
|
|
238
|
-
|
|
294
|
+
// Written out, not left as an example, once the language is known: the
|
|
295
|
+
// field is the whole of the feature, and the folder beside it already
|
|
296
|
+
// holds a page.
|
|
297
|
+
translation
|
|
298
|
+
? ` translations: { ${key(translation)}: ${quote(`docs/${slug}-${translation}`)} },`
|
|
299
|
+
: ` // translations: { fr: ${quote(`docs/${slug}-fr`)} },`,
|
|
239
300
|
' },',
|
|
240
301
|
' ],',
|
|
241
302
|
'',
|
|
@@ -353,11 +414,12 @@ function versionSlug(version) {
|
|
|
353
414
|
}
|
|
354
415
|
|
|
355
416
|
/**
|
|
356
|
-
*
|
|
357
|
-
*
|
|
417
|
+
* Home page of the project, in the language of the site.
|
|
418
|
+
*
|
|
419
|
+
* @param {{ name: string, docs: boolean, slug: string, translation: string }} answers
|
|
358
420
|
* @returns {string}
|
|
359
421
|
*/
|
|
360
|
-
const renderIndex = (name, docs) => `---
|
|
422
|
+
const renderIndex = ({ name, docs, slug, translation }) => `---
|
|
361
423
|
title: Introduction
|
|
362
424
|
description: Documentation of ${name}.
|
|
363
425
|
date: ${new Date().toISOString().slice(0, 10)}
|
|
@@ -378,6 +440,20 @@ without appearing in the URL.
|
|
|
378
440
|
|
|
379
441
|
See the [installation guide](/guide/installation/).
|
|
380
442
|
${
|
|
443
|
+
translation
|
|
444
|
+
? `
|
|
445
|
+
## In ${languageName(translation)}
|
|
446
|
+
|
|
447
|
+
The same pages live in \`docs/${slug}-${translation}/\`, and the language switcher
|
|
448
|
+
in the header moves between them.
|
|
449
|
+
|
|
450
|
+
A page with no twin there does not exist in that language: it stays out of the
|
|
451
|
+
menu and out of the sitemap, and the switcher names the language without
|
|
452
|
+
offering it. The installation page is in that case — write
|
|
453
|
+
\`docs/${slug}-${translation}/01-guide/01-installation.md\` and it appears.
|
|
454
|
+
${docs ? '\nThe [languages guide](/docpensieve/guide/languages/) covers the rest.\n' : ''}`
|
|
455
|
+
: ''
|
|
456
|
+
}${
|
|
381
457
|
docs
|
|
382
458
|
? `
|
|
383
459
|
## Learning DocPensieve
|
|
@@ -389,6 +465,70 @@ tool that builds this site, installed along with it. Delete its folder,
|
|
|
389
465
|
: ''
|
|
390
466
|
}`;
|
|
391
467
|
|
|
468
|
+
/**
|
|
469
|
+
* Home page of the second language.
|
|
470
|
+
*
|
|
471
|
+
* French is written out, the tool shipping its wording too. Any other
|
|
472
|
+
* language gets the page in English, saying in its first line that it is
|
|
473
|
+
* there to be translated: a copy passing for a translation is the one failure
|
|
474
|
+
* this feature invites — the reader gets English under an address that
|
|
475
|
+
* promised their language, and nothing reports it.
|
|
476
|
+
*
|
|
477
|
+
* Only the home page is written. Its twin, the installation page, is left
|
|
478
|
+
* untranslated on purpose: it is what shows that an untranslated page does
|
|
479
|
+
* not exist in that language, which no sentence explains as well as the menu
|
|
480
|
+
* that lacks it.
|
|
481
|
+
*
|
|
482
|
+
* @param {{ name: string, slug: string, translation: string }} answers
|
|
483
|
+
* @returns {string}
|
|
484
|
+
*/
|
|
485
|
+
function renderTranslatedIndex({ name, slug, translation }) {
|
|
486
|
+
const french = translation.toLowerCase().split('-')[0] === 'fr';
|
|
487
|
+
const head = `---
|
|
488
|
+
title: Introduction
|
|
489
|
+
description: ${french ? `Documentation de ${name}.` : `Documentation of ${name}.`}
|
|
490
|
+
date: ${new Date().toISOString().slice(0, 10)}
|
|
491
|
+
|
|
492
|
+
jsonld:
|
|
493
|
+
type: TechArticle
|
|
494
|
+
breadcrumbs: true
|
|
495
|
+
---
|
|
496
|
+
|
|
497
|
+
# ${name}
|
|
498
|
+
`;
|
|
499
|
+
|
|
500
|
+
if (french) {
|
|
501
|
+
return `${head}
|
|
502
|
+
Bienvenue dans la documentation.
|
|
503
|
+
|
|
504
|
+
## Une page et sa jumelle
|
|
505
|
+
|
|
506
|
+
Cette page est la version française de \`docs/${slug}/index.md\`. Le sélecteur
|
|
507
|
+
de langue, dans l'en-tête, passe de l'une à l'autre.
|
|
508
|
+
|
|
509
|
+
La page d'installation, elle, n'est pas traduite : elle **n'existe pas** en
|
|
510
|
+
français. Elle ne figure ni dans le menu ni dans le plan du site, et le
|
|
511
|
+
sélecteur la nomme sans la proposer. Écrivez
|
|
512
|
+
\`docs/${slug}-${translation}/01-guide/01-installation.md\` pour la voir apparaître.
|
|
513
|
+
`;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
return `${head}
|
|
517
|
+
**Replace this page with your translation.** It is the ${languageName(translation)}
|
|
518
|
+
twin of \`docs/${slug}/index.md\`, written in English so that the site builds:
|
|
519
|
+
left as it is, a reader who picks ${languageName(translation, translation)} gets English.
|
|
520
|
+
|
|
521
|
+
## One page, two languages
|
|
522
|
+
|
|
523
|
+
The language switcher in the header moves between this page and its twin.
|
|
524
|
+
|
|
525
|
+
The installation page has no twin here, so it does not exist in this language:
|
|
526
|
+
it stays out of the menu and out of the sitemap, and the switcher names the
|
|
527
|
+
language without offering it. Write
|
|
528
|
+
\`docs/${slug}-${translation}/01-guide/01-installation.md\` and it appears.
|
|
529
|
+
`;
|
|
530
|
+
}
|
|
531
|
+
|
|
392
532
|
/** @returns {string} Sample page, showing ordering and highlighting. */
|
|
393
533
|
const renderGuide = () => `---
|
|
394
534
|
title: Installation
|
|
@@ -478,11 +618,14 @@ async function installDocumentation(source, target, slug) {
|
|
|
478
618
|
* @param {string} [dir] Target folder, created if needed.
|
|
479
619
|
* @param {{
|
|
480
620
|
* name?: string, theme?: string, siteUrl?: string, version?: string,
|
|
481
|
-
* yes?: boolean, force?: boolean, minimal?: boolean,
|
|
482
|
-
* }} [options] `minimal` leaves DocPensieve's documentation out of the site
|
|
483
|
-
*
|
|
484
|
-
* @
|
|
485
|
-
*
|
|
621
|
+
* translation?: string, yes?: boolean, force?: boolean, minimal?: boolean,
|
|
622
|
+
* }} [options] `minimal` leaves DocPensieve's documentation out of the site;
|
|
623
|
+
* `translation` is the code of a second language, `fr` for instance.
|
|
624
|
+
* @returns {Promise<{
|
|
625
|
+
* dir: string, theme: string, docs: boolean, translation: string,
|
|
626
|
+
* }>}
|
|
627
|
+
* @throws {DocPensieveError} Unknown framework, unknown language, project
|
|
628
|
+
* already initialised, or documentation to install missing.
|
|
486
629
|
*/
|
|
487
630
|
export async function init(dir = '.', options = {}) {
|
|
488
631
|
const target = path.resolve(dir);
|
|
@@ -504,6 +647,14 @@ export async function init(dir = '.', options = {}) {
|
|
|
504
647
|
});
|
|
505
648
|
}
|
|
506
649
|
|
|
650
|
+
// Checked here for the same reason as the framework: a code refused after
|
|
651
|
+
// five questions would be five questions wasted.
|
|
652
|
+
if (options.translation && !isLanguageCode(options.translation)) {
|
|
653
|
+
throw new DocPensieveError(`"${options.translation}" does not name a language.`, {
|
|
654
|
+
hint: 'Write the code, not the name: "fr" for French, "pt-BR", "zh-Hans". It becomes the lang of the document and a segment of the address.',
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
|
|
507
658
|
const answers = await collect(options);
|
|
508
659
|
|
|
509
660
|
// Located before anything is written: a project left half set up would be
|
|
@@ -535,8 +686,21 @@ export async function init(dir = '.', options = {}) {
|
|
|
535
686
|
|
|
536
687
|
await mkdir(path.join(docsDir, '01-guide'), { recursive: true });
|
|
537
688
|
await writeFile(configPath, renderConfig(answers), 'utf8');
|
|
538
|
-
await writeFile(
|
|
689
|
+
await writeFile(
|
|
690
|
+
path.join(docsDir, 'index.md'),
|
|
691
|
+
renderIndex({ name: answers.name, docs: answers.docs, slug, translation: answers.translation }),
|
|
692
|
+
'utf8',
|
|
693
|
+
);
|
|
539
694
|
await writeFile(path.join(docsDir, '01-guide', '01-installation.md'), renderGuide(), 'utf8');
|
|
695
|
+
if (answers.translation) {
|
|
696
|
+
const folder = path.join(target, 'docs', `${slug}-${answers.translation}`);
|
|
697
|
+
await mkdir(folder, { recursive: true });
|
|
698
|
+
await writeFile(
|
|
699
|
+
path.join(folder, 'index.md'),
|
|
700
|
+
renderTranslatedIndex({ name: answers.name, slug, translation: answers.translation }),
|
|
701
|
+
'utf8',
|
|
702
|
+
);
|
|
703
|
+
}
|
|
540
704
|
if (documentation) {
|
|
541
705
|
await installDocumentation(documentation, path.join(docsDir, DOCS_FOLDER), slug);
|
|
542
706
|
}
|
|
@@ -548,14 +712,31 @@ export async function init(dir = '.', options = {}) {
|
|
|
548
712
|
console.log(` ${CONFIG_FILENAME}`);
|
|
549
713
|
console.log(` docs/${slug}/index.md`);
|
|
550
714
|
console.log(` docs/${slug}/01-guide/01-installation.md`);
|
|
715
|
+
if (answers.translation) {
|
|
716
|
+
const state =
|
|
717
|
+
answers.translation.toLowerCase().split('-')[0] === 'fr'
|
|
718
|
+
? `in ${languageName(answers.translation)}`
|
|
719
|
+
: 'to translate';
|
|
720
|
+
console.log(` docs/${slug}-${answers.translation}/index.md the home page, ${state}`);
|
|
721
|
+
}
|
|
551
722
|
if (documentation) {
|
|
552
723
|
console.log(` docs/${slug}/${DOCS_FOLDER}/ DocPensieve's documentation, to delete when done`);
|
|
553
724
|
}
|
|
554
725
|
for (const line of stylesheets) console.log(` ${line}`);
|
|
555
726
|
console.log(`\nTheme: ${answers.theme} — ${FRAMEWORK_LABELS[answers.theme]}`);
|
|
727
|
+
if (answers.translation) {
|
|
728
|
+
console.log(
|
|
729
|
+
`Second language: ${languageName(answers.translation)} (${answers.translation}) — served under /${answers.translation}/`,
|
|
730
|
+
);
|
|
731
|
+
}
|
|
556
732
|
console.log('\nNext: npx docpensieve dev');
|
|
557
733
|
|
|
558
|
-
return {
|
|
734
|
+
return {
|
|
735
|
+
dir: target,
|
|
736
|
+
theme: answers.theme,
|
|
737
|
+
docs: answers.docs,
|
|
738
|
+
translation: answers.translation,
|
|
739
|
+
};
|
|
559
740
|
}
|
|
560
741
|
|
|
561
742
|
/**
|
|
@@ -46,6 +46,7 @@ npx docpensieve init my-site --yes --name "My documentation"
|
|
|
46
46
|
| `-t, --theme <framework>` | `tailwind` or `custom` |
|
|
47
47
|
| `-u, --site-url <url>` | Public URL, from which the deployment prefix is derived |
|
|
48
48
|
| `--version-name <version>` | First version, `1.0` for instance |
|
|
49
|
+
| `--translation <code>` | Code of a second language, `fr` for instance |
|
|
49
50
|
| `-y, --yes` | Accepts the defaults without a dialogue |
|
|
50
51
|
| `-f, --force` | Overwrites an existing configuration |
|
|
51
52
|
| `--minimal` | Leaves DocPensieve's documentation out of the site |
|
|
@@ -13,6 +13,19 @@ jsonld:
|
|
|
13
13
|
A version can be published in several languages. Each one is a folder of pages
|
|
14
14
|
of its own, standing beside the version it translates.
|
|
15
15
|
|
|
16
|
+
## Starting from init
|
|
17
|
+
|
|
18
|
+
`init` asks whether the site will be in several languages. Answer yes, give a
|
|
19
|
+
code, and the project comes out wired: the folder beside your pages, the field
|
|
20
|
+
in the configuration, and a home page in that language to start from.
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npx docpensieve init my-site --translation fr
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Everything below is what that answer sets up — and what to do when you add a
|
|
27
|
+
language to a project that already exists.
|
|
28
|
+
|
|
16
29
|
## Declaring a translation
|
|
17
30
|
|
|
18
31
|
```js
|
|
@@ -29,6 +29,7 @@ npx docpensieve init [dir]
|
|
|
29
29
|
| `-t, --theme <framework>` | `tailwind` or `custom` |
|
|
30
30
|
| `-u, --site-url <url>` | Public URL of the site |
|
|
31
31
|
| `--version-name <version>` | First version, `1.0` for instance |
|
|
32
|
+
| `--translation <code>` | Code of a second language, `fr` |
|
|
32
33
|
| `-y, --yes` | Accepts the defaults without a dialogue |
|
|
33
34
|
| `-f, --force` | Overwrites an existing configuration |
|
|
34
35
|
| `--minimal` | Leaves DocPensieve's documentation out |
|
|
@@ -346,6 +346,39 @@ a language nobody thought of.
|
|
|
346
346
|
|
|
347
347
|
**Returns** `'ltr' \| 'rtl'` — `ltr` when the language is unknown — the safe default, and what every page did before this existed.
|
|
348
348
|
|
|
349
|
+
### `isLanguageCode`
|
|
350
|
+
|
|
351
|
+
`isLanguageCode(value)`
|
|
352
|
+
|
|
353
|
+
Whether a string names a language, as BCP 47 and CLDR understand it.
|
|
354
|
+
|
|
355
|
+
The standard is BCP 47, and `Intl` carries it: a regex of our own refused
|
|
356
|
+
`zh-Hans-CN`, which is valid, and accepted shapes that are not. Well formed
|
|
357
|
+
is not the same as real, though — BCP 47 allows a language subtag of five
|
|
358
|
+
to eight letters, so `francais` passes that check and would land in the
|
|
359
|
+
markup as `lang="francais"`, a value no browser maps to a language. CLDR
|
|
360
|
+
knows which tags name one; the subtag alone is asked, so that a region, a
|
|
361
|
+
script or a private extension does not get in the way.
|
|
362
|
+
|
|
363
|
+
| Parameter | Type | |
|
|
364
|
+
| --- | --- | --- |
|
|
365
|
+
| `value` | `string` | |
|
|
366
|
+
|
|
367
|
+
**Returns** `boolean`
|
|
368
|
+
|
|
369
|
+
### `languageName`
|
|
370
|
+
|
|
371
|
+
`languageName(code, [inLang])`
|
|
372
|
+
|
|
373
|
+
Name of a language, written in a language.
|
|
374
|
+
|
|
375
|
+
| Parameter | Type | |
|
|
376
|
+
| --- | --- | --- |
|
|
377
|
+
| `code` | `string` | Language code. |
|
|
378
|
+
| `[inLang]` | `string` | Language the name is written in. |
|
|
379
|
+
|
|
380
|
+
**Returns** `string` — The name, or the code itself when nothing names it.
|
|
381
|
+
|
|
349
382
|
## `@docpensieve/core`
|
|
350
383
|
|
|
351
384
|
Configuration, loading, compilation, structured data and generation.
|
|
@@ -1211,11 +1244,11 @@ Sets up a documentation project.
|
|
|
1211
1244
|
| Parameter | Type | |
|
|
1212
1245
|
| --- | --- | --- |
|
|
1213
1246
|
| `[dir]` | `string` | Target folder, created if needed. |
|
|
1214
|
-
| `[options]` | `{ name?: string, theme?: string, siteUrl?: string, version?: string, yes?: boolean, force?: boolean, minimal?: boolean, }` | `minimal` leaves DocPensieve's documentation out of the site. |
|
|
1247
|
+
| `[options]` | `{ name?: string, theme?: string, siteUrl?: string, version?: string, translation?: string, yes?: boolean, force?: boolean, minimal?: boolean, }` | `minimal` leaves DocPensieve's documentation out of the site; `translation` is the code of a second language, `fr` for instance. |
|
|
1215
1248
|
|
|
1216
|
-
**Returns** `Promise<{ dir: string, theme: string, docs: boolean }>`
|
|
1249
|
+
**Returns** `Promise<{ dir: string, theme: string, docs: boolean, translation: string, }>`
|
|
1217
1250
|
|
|
1218
|
-
**Throws** `DocPensieveError` — Unknown framework, project already initialised, or documentation to install missing.
|
|
1251
|
+
**Throws** `DocPensieveError` — Unknown framework, unknown language, project already initialised, or documentation to install missing.
|
|
1219
1252
|
|
|
1220
1253
|
### `serve`
|
|
1221
1254
|
|
package/starter/05-whats-new.md
CHANGED
|
@@ -20,11 +20,27 @@ written.
|
|
|
20
20
|
|
|
21
21
|
## Already there
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
### init asks about languages
|
|
24
|
+
|
|
25
|
+
`init` now asks whether the site will be in several languages, and sets the
|
|
26
|
+
answer up: the folder beside your pages, `translations` written out in the
|
|
27
|
+
configuration, and a home page in that language to start from. The second
|
|
28
|
+
sample page is deliberately left untranslated, so that the menu shows what an
|
|
29
|
+
untranslated page does — nothing, in that language.
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npx docpensieve init my-site --translation fr
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
A code is a BCP 47 one: `fr`, `pt-BR`, `zh-Hans`. French and English ship with
|
|
36
|
+
their wording for the shell; any other language keeps the English wording
|
|
37
|
+
until the `ui` field gives it its own.
|
|
38
|
+
|
|
39
|
+
[Languages](./guide/languages/) · [The init options](./reference/cli/)
|
|
25
40
|
|
|
26
41
|
## For a 0.4 project
|
|
27
42
|
|
|
28
|
-
Nothing to change: a 0.4 configuration builds as it is
|
|
43
|
+
Nothing to change: a 0.4 configuration builds as it is, and a site already in
|
|
44
|
+
two languages is untouched — the question only shapes a **new** project.
|
|
29
45
|
[Migrate from latest to beta](./guide/migrate-to-beta/) lists what changes on
|
|
30
46
|
its own, and what is worth turning on.
|
package/types/commands/init.d.ts
CHANGED
|
@@ -9,17 +9,21 @@
|
|
|
9
9
|
* @param {string} [dir] Target folder, created if needed.
|
|
10
10
|
* @param {{
|
|
11
11
|
* name?: string, theme?: string, siteUrl?: string, version?: string,
|
|
12
|
-
* yes?: boolean, force?: boolean, minimal?: boolean,
|
|
13
|
-
* }} [options] `minimal` leaves DocPensieve's documentation out of the site
|
|
14
|
-
*
|
|
15
|
-
* @
|
|
16
|
-
*
|
|
12
|
+
* translation?: string, yes?: boolean, force?: boolean, minimal?: boolean,
|
|
13
|
+
* }} [options] `minimal` leaves DocPensieve's documentation out of the site;
|
|
14
|
+
* `translation` is the code of a second language, `fr` for instance.
|
|
15
|
+
* @returns {Promise<{
|
|
16
|
+
* dir: string, theme: string, docs: boolean, translation: string,
|
|
17
|
+
* }>}
|
|
18
|
+
* @throws {DocPensieveError} Unknown framework, unknown language, project
|
|
19
|
+
* already initialised, or documentation to install missing.
|
|
17
20
|
*/
|
|
18
21
|
export declare function init(dir?: string, options?: {
|
|
19
22
|
name?: string;
|
|
20
23
|
theme?: string;
|
|
21
24
|
siteUrl?: string;
|
|
22
25
|
version?: string;
|
|
26
|
+
translation?: string;
|
|
23
27
|
yes?: boolean;
|
|
24
28
|
force?: boolean;
|
|
25
29
|
minimal?: boolean;
|
|
@@ -27,4 +31,5 @@ export declare function init(dir?: string, options?: {
|
|
|
27
31
|
dir: string;
|
|
28
32
|
theme: string;
|
|
29
33
|
docs: boolean;
|
|
34
|
+
translation: string;
|
|
30
35
|
}>;
|