@rr0/cms 0.3.77 → 0.3.78

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.
@@ -29,7 +29,12 @@ export declare class RR0ContextImpl extends SsgContextImpl {
29
29
  readonly images: Set<string>;
30
30
  protected readonly fileMap: Map<string, FileContents>;
31
31
  place: PlaceContext;
32
+ /**
33
+ * The language of a passage that differs from its page's (<blockquote lang="en"> in a French page).
34
+ */
35
+ protected localeOverride?: string;
32
36
  constructor(locale: string, time: TimeContext, config: FileWriteConfig, people?: any, currentFile?: FileContents | undefined, _messages?: RR0Messages, cms?: CMSContext);
37
+ get locale(): string;
33
38
  get messages(): RR0Messages;
34
39
  read(filePath: string): FileContents;
35
40
  clone(locale?: string): RR0ContextImpl;
@@ -13,6 +13,10 @@ export class RR0ContextImpl extends SsgContextImpl {
13
13
  this.fileMap = new Map();
14
14
  this.place = new PlaceContext(locale, this.messages.context.place);
15
15
  }
16
+ get locale() {
17
+ var _a;
18
+ return (_a = this.localeOverride) !== null && _a !== void 0 ? _a : super.locale;
19
+ }
16
20
  get messages() {
17
21
  let messages = this._messages;
18
22
  if (!messages) {
@@ -43,7 +47,10 @@ export class RR0ContextImpl extends SsgContextImpl {
43
47
  }
44
48
  clone(locale = this.locale) {
45
49
  var _a;
46
- return new RR0ContextImpl(locale, this.time.clone(), this.config, (_a = this.people) === null || _a === void 0 ? void 0 : _a.clone(), this._file, this.messages, this.cms);
50
+ const sameLocale = locale === this.locale;
51
+ const clone = new RR0ContextImpl(locale, this.time.clone(), this.config, (_a = this.people) === null || _a === void 0 ? void 0 : _a.clone(), this._file, sameLocale ? this._messages : undefined, this.cms);
52
+ clone.localeOverride = sameLocale ? this.localeOverride : locale;
53
+ return clone;
47
54
  }
48
55
  toString() {
49
56
  var _a;
@@ -30,7 +30,9 @@ export class LanguageReplaceCommand extends DomReplaceCommand {
30
30
  }
31
31
  }
32
32
  else {
33
- pageLang = "fr";
33
+ // A file without variant is French, unless it declares otherwise (an English document not translated yet)
34
+ const declared = inputFile.document.querySelector("meta[http-equiv='content-language' i]");
35
+ pageLang = (declared === null || declared === void 0 ? void 0 : declared.content.trim().substring(0, 2).toLowerCase()) || "fr";
34
36
  }
35
37
  inputFile.document.documentElement.lang = inputFile.lang.lang = pageLang;
36
38
  const langVariants = variants.length == 1 && variants[0] == "" ? [pageLang == "fr" ? "en" : "fr"] : variants;
@@ -8,6 +8,12 @@ describe("LanguageReplaceCommand", () => {
8
8
  await command.execute(context);
9
9
  expect(context.file.contents).toBe(`<html lang="fr"><head><meta name="generator" content="ssg-api"></head><body><span id="alternate"><a href="/${cmsTestUtil.filePath(cmsTestUtil.time.filePath("1/9/6/8/CondonReport/index.html"))}">English version</a></span></body></html>`);
10
10
  });
11
+ test("declared language of a file without variant", async () => {
12
+ const context = cmsTestUtil.time.newHtmlContext("1/9/9/0/08/index.html", `<meta http-equiv="content-language" content="en"><span id="alternate"/>`);
13
+ await new LanguageReplaceCommand().execute(context);
14
+ expect(context.file.lang.lang).toBe("en");
15
+ expect(context.file.document.documentElement.lang).toBe("en");
16
+ });
11
17
  test("add english translation", async () => {
12
18
  const context = cmsTestUtil.time.newHtmlContext("0/6/4/0/ChronicaMinor/index_fr.html", `<span id="alternate"/>`);
13
19
  const command = new LanguageReplaceCommand();
@@ -20,7 +20,11 @@ export declare class TimeReplacer implements DomReplacement<HtmlRR0Context, HTML
20
20
  * @return The text that precedes an element in its sentence.
21
21
  */
22
22
  static precedingText(el: Element): string;
23
- replacement(context: HtmlRR0Context, origEl: HTMLTimeElement): Promise<HTMLElement>;
23
+ /**
24
+ * @return The language of the passage holding an element, if it differs from its page's.
25
+ */
26
+ static passageLang(context: HtmlRR0Context, el: Element): string | undefined;
27
+ replacement(pageContext: HtmlRR0Context, origEl: HTMLTimeElement): Promise<HTMLElement>;
24
28
  /**
25
29
  * Renders a range of durations ("P10M/12M") as "from 10 to 12 minutes".
26
30
  */
@@ -24,12 +24,23 @@ export class TimeReplacer {
24
24
  }
25
25
  return (_b = (_a = node.previousSibling) === null || _a === void 0 ? void 0 : _a.textContent) !== null && _b !== void 0 ? _b : "";
26
26
  }
27
- async replacement(context, origEl) {
27
+ /**
28
+ * @return The language of the passage holding an element, if it differs from its page's.
29
+ */
30
+ static passageLang(context, el) {
31
+ var _a, _b;
32
+ const lang = (_b = (_a = el.closest("[lang]")) === null || _a === void 0 ? void 0 : _a.getAttribute("lang")) === null || _b === void 0 ? void 0 : _b.trim().substring(0, 2).toLowerCase();
33
+ return lang && lang !== context.locale.substring(0, 2) ? lang : undefined;
34
+ }
35
+ async replacement(pageContext, origEl) {
28
36
  let replacement;
29
37
  if (origEl.dateTime || origEl.dataset.format === "none") { // Already done, or not to be interpreted
30
38
  replacement = origEl;
31
39
  }
32
40
  else {
41
+ // A time in a passage of another language ("<blockquote lang='en'>") is rendered in that language
42
+ const passageLang = TimeReplacer.passageLang(pageContext, origEl);
43
+ const context = passageLang ? pageContext.clone(passageLang) : pageContext;
33
44
  const previousContext = origEl.dataset.context === "none" ? undefined : context.clone();
34
45
  const timeStr = origEl.textContent;
35
46
  const { prefix, value, suffix } = this.completer.split(timeStr);
@@ -72,6 +83,9 @@ export class TimeReplacer {
72
83
  wrapper.append(prefix, replacement, suffix);
73
84
  replacement = wrapper;
74
85
  }
86
+ if (context !== pageContext) {
87
+ pageContext.time = context.time; // The next times of the page follow this one
88
+ }
75
89
  context.debug("\tReplacing time", origEl.outerHTML, "with", ObjectUtils.asSet(replacement).outerHTML);
76
90
  }
77
91
  return replacement;
@@ -236,6 +236,11 @@ describe("TimeReplacer", async () => {
236
236
  test("but not without one", async () => {
237
237
  expect(await renderAfter("fr", "<time>2005</time>. Puis, <time>2006</time>")).toBe("l'année suivante");
238
238
  });
239
+ test("in the language of its passage", async () => {
240
+ expect(await renderAfter("fr", "<time>2005</time>. <q lang=\"en\">in <time>2006</time></q>")).toBe("2006");
241
+ expect(await renderAfter("fr", "<time>2005</time>. <q lang=\"en\">Then <time>2006</time></q>")).toBe("the year after");
242
+ expect(await renderAfter("fr", "<q lang=\"en\"><time>2006</time></q>. <time>2007</time>")).toBe("l'année suivante");
243
+ });
239
244
  test("including the one of an open interval", async () => {
240
245
  expect(await renderAfter("fr", "<time>2005</time>. <time>2006/</time>")).toBe("à partir de 2006");
241
246
  expect(await renderAfter("en", "<time>2005</time>. <time>2006/</time>")).toBe("starting 2006");
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@rr0/cms",
3
3
  "type": "module",
4
4
  "author": "Jérôme Beau <rr0@rr0.org> (https://rr0.org)",
5
- "version": "0.3.77",
5
+ "version": "0.3.78",
6
6
  "description": "RR0 Content Management System (CMS)",
7
7
  "exports": "./dist/index.js",
8
8
  "types": "./dist/index.d.ts",