diff --git a/CHANGELOG.md b/CHANGELOG.md index 00eb69d087..3561f05d19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,8 @@ Legends: - Fixed syntax-highlighted code having less than 4.5:1 color contrast: comments in the light code block theme (`github-light-default`) were 4.36:1 in the Fluent theme and now use the muted text color of the theme, `#656d76` (5.03:1), in PR [#5893](https://github.com/microsoft/BotFramework-WebChat/pull/5893), by [@OEvgeny](https://github.com/OEvgeny) - Fixed code blocks becoming empty when Web Chat is rendered with messages already in the store, such as again with the same store or from a saved state: code is shown without highlighting until the syntax highlighter is loaded, in PR [#5893](https://github.com/microsoft/BotFramework-WebChat/pull/5893), by [@OEvgeny](https://github.com/OEvgeny) - Fixed the native controls of audio and video players, such as volume and "more options", not being reachable with TAB and SHIFT + TAB inside an activity: the focus now moves through every control of the player and wraps within the activity; the players of audio, video, and animation cards are now named after the card title, fixes [#5738](https://github.com/microsoft/BotFramework-WebChat/issues/5738) and [#5709](https://github.com/microsoft/BotFramework-WebChat/issues/5709), in PR [#5891](https://github.com/microsoft/BotFramework-WebChat/pull/5891), by [@OEvgeny](https://github.com/OEvgeny) +- Fixed code samples, such as in the "View code" dialog, titled "Javascript" instead of "JavaScript": common programming languages and their aliases, such as `js`, now show their proper names, which can be overridden via `overrideLocalizedStrings` (`PROGRAMMING_LANGUAGE_*`), in PR [#5896](https://github.com/microsoft/BotFramework-WebChat/pull/5896), by [@OEvgeny](https://github.com/OEvgeny) +- Fixed code not being highlighted when the programming language is not in lowercase, such as `TS`, in PR [#5896](https://github.com/microsoft/BotFramework-WebChat/pull/5896), by [@OEvgeny](https://github.com/OEvgeny) ## [4.19.2] - 2026-10-08 diff --git a/__tests__/html2/activity/collapsible.copilot.dark.html.snap-4.png b/__tests__/html2/activity/collapsible.copilot.dark.html.snap-4.png index fb12845e93..e5b83fbd00 100644 Binary files a/__tests__/html2/activity/collapsible.copilot.dark.html.snap-4.png and b/__tests__/html2/activity/collapsible.copilot.dark.html.snap-4.png differ diff --git a/__tests__/html2/activity/collapsible.copilot.dark.html.snap-5.png b/__tests__/html2/activity/collapsible.copilot.dark.html.snap-5.png index 9601cf7907..6a3d993795 100644 Binary files a/__tests__/html2/activity/collapsible.copilot.dark.html.snap-5.png and b/__tests__/html2/activity/collapsible.copilot.dark.html.snap-5.png differ diff --git a/__tests__/html2/activity/collapsible.copilot.dark.html.snap-6.png b/__tests__/html2/activity/collapsible.copilot.dark.html.snap-6.png index 2dcc0667db..2cc98e4230 100644 Binary files a/__tests__/html2/activity/collapsible.copilot.dark.html.snap-6.png and b/__tests__/html2/activity/collapsible.copilot.dark.html.snap-6.png differ diff --git a/__tests__/html2/activity/collapsible.copilot.dark.html.snap-7.png b/__tests__/html2/activity/collapsible.copilot.dark.html.snap-7.png index d16c21abc1..9b63d9f3d7 100644 Binary files a/__tests__/html2/activity/collapsible.copilot.dark.html.snap-7.png and b/__tests__/html2/activity/collapsible.copilot.dark.html.snap-7.png differ diff --git a/__tests__/html2/activity/collapsible.copilot.html.snap-4.png b/__tests__/html2/activity/collapsible.copilot.html.snap-4.png index a2af2a1fa6..efbcd24bd9 100644 Binary files a/__tests__/html2/activity/collapsible.copilot.html.snap-4.png and b/__tests__/html2/activity/collapsible.copilot.html.snap-4.png differ diff --git a/__tests__/html2/activity/collapsible.copilot.html.snap-5.png b/__tests__/html2/activity/collapsible.copilot.html.snap-5.png index 56cc54c675..4617a84ece 100644 Binary files a/__tests__/html2/activity/collapsible.copilot.html.snap-5.png and b/__tests__/html2/activity/collapsible.copilot.html.snap-5.png differ diff --git a/__tests__/html2/activity/collapsible.copilot.html.snap-6.png b/__tests__/html2/activity/collapsible.copilot.html.snap-6.png index 900570c31d..c5341c4b0f 100644 Binary files a/__tests__/html2/activity/collapsible.copilot.html.snap-6.png and b/__tests__/html2/activity/collapsible.copilot.html.snap-6.png differ diff --git a/__tests__/html2/activity/collapsible.copilot.html.snap-7.png b/__tests__/html2/activity/collapsible.copilot.html.snap-7.png index 22348de02a..e5472e8467 100644 Binary files a/__tests__/html2/activity/collapsible.copilot.html.snap-7.png and b/__tests__/html2/activity/collapsible.copilot.html.snap-7.png differ diff --git a/__tests__/html2/activity/collapsible.fluent.dark.html.snap-4.png b/__tests__/html2/activity/collapsible.fluent.dark.html.snap-4.png index 5ce1203572..0b0d64cccc 100644 Binary files a/__tests__/html2/activity/collapsible.fluent.dark.html.snap-4.png and b/__tests__/html2/activity/collapsible.fluent.dark.html.snap-4.png differ diff --git a/__tests__/html2/activity/collapsible.fluent.dark.html.snap-5.png b/__tests__/html2/activity/collapsible.fluent.dark.html.snap-5.png index 065bd68bed..62d65471b8 100644 Binary files a/__tests__/html2/activity/collapsible.fluent.dark.html.snap-5.png and b/__tests__/html2/activity/collapsible.fluent.dark.html.snap-5.png differ diff --git a/__tests__/html2/activity/collapsible.fluent.dark.html.snap-6.png b/__tests__/html2/activity/collapsible.fluent.dark.html.snap-6.png index 6df2dd845b..09b93d2f32 100644 Binary files a/__tests__/html2/activity/collapsible.fluent.dark.html.snap-6.png and b/__tests__/html2/activity/collapsible.fluent.dark.html.snap-6.png differ diff --git a/__tests__/html2/activity/collapsible.fluent.dark.html.snap-7.png b/__tests__/html2/activity/collapsible.fluent.dark.html.snap-7.png index cdca397a48..f0d6937471 100644 Binary files a/__tests__/html2/activity/collapsible.fluent.dark.html.snap-7.png and b/__tests__/html2/activity/collapsible.fluent.dark.html.snap-7.png differ diff --git a/__tests__/html2/activity/collapsible.fluent.dark.html.snap-8.png b/__tests__/html2/activity/collapsible.fluent.dark.html.snap-8.png index 1fdfb19056..29699e6c0d 100644 Binary files a/__tests__/html2/activity/collapsible.fluent.dark.html.snap-8.png and b/__tests__/html2/activity/collapsible.fluent.dark.html.snap-8.png differ diff --git a/__tests__/html2/activity/collapsible.fluent.html.snap-4.png b/__tests__/html2/activity/collapsible.fluent.html.snap-4.png index 52f83b856d..db75dde205 100644 Binary files a/__tests__/html2/activity/collapsible.fluent.html.snap-4.png and b/__tests__/html2/activity/collapsible.fluent.html.snap-4.png differ diff --git a/__tests__/html2/activity/collapsible.fluent.html.snap-5.png b/__tests__/html2/activity/collapsible.fluent.html.snap-5.png index 42c119ab51..a3f0e5bf87 100644 Binary files a/__tests__/html2/activity/collapsible.fluent.html.snap-5.png and b/__tests__/html2/activity/collapsible.fluent.html.snap-5.png differ diff --git a/__tests__/html2/activity/collapsible.fluent.html.snap-6.png b/__tests__/html2/activity/collapsible.fluent.html.snap-6.png index c677c934a6..a5730cfb8c 100644 Binary files a/__tests__/html2/activity/collapsible.fluent.html.snap-6.png and b/__tests__/html2/activity/collapsible.fluent.html.snap-6.png differ diff --git a/__tests__/html2/activity/collapsible.fluent.html.snap-7.png b/__tests__/html2/activity/collapsible.fluent.html.snap-7.png index 55a7150b13..d94e7cc756 100644 Binary files a/__tests__/html2/activity/collapsible.fluent.html.snap-7.png and b/__tests__/html2/activity/collapsible.fluent.html.snap-7.png differ diff --git a/__tests__/html2/activity/collapsible.fluent.html.snap-8.png b/__tests__/html2/activity/collapsible.fluent.html.snap-8.png index a45c576afe..6ad35482d5 100644 Binary files a/__tests__/html2/activity/collapsible.fluent.html.snap-8.png and b/__tests__/html2/activity/collapsible.fluent.html.snap-8.png differ diff --git a/__tests__/html2/activity/collapsible.html.snap-4.png b/__tests__/html2/activity/collapsible.html.snap-4.png index a321c934db..8d5a98a907 100644 Binary files a/__tests__/html2/activity/collapsible.html.snap-4.png and b/__tests__/html2/activity/collapsible.html.snap-4.png differ diff --git a/__tests__/html2/activity/collapsible.html.snap-5.png b/__tests__/html2/activity/collapsible.html.snap-5.png index 0ccbf73ada..f48d401bec 100644 Binary files a/__tests__/html2/activity/collapsible.html.snap-5.png and b/__tests__/html2/activity/collapsible.html.snap-5.png differ diff --git a/__tests__/html2/activity/collapsible.html.snap-6.png b/__tests__/html2/activity/collapsible.html.snap-6.png index c0d150ec7e..4a818bdd26 100644 Binary files a/__tests__/html2/activity/collapsible.html.snap-6.png and b/__tests__/html2/activity/collapsible.html.snap-6.png differ diff --git a/__tests__/html2/activity/collapsible.html.snap-7.png b/__tests__/html2/activity/collapsible.html.snap-7.png index 2a5686962e..12cc7c457d 100644 Binary files a/__tests__/html2/activity/collapsible.html.snap-7.png and b/__tests__/html2/activity/collapsible.html.snap-7.png differ diff --git a/__tests__/html2/activity/collapsible.html.snap-8.png b/__tests__/html2/activity/collapsible.html.snap-8.png index 41aae831ad..ae8bd583c2 100644 Binary files a/__tests__/html2/activity/collapsible.html.snap-8.png and b/__tests__/html2/activity/collapsible.html.snap-8.png differ diff --git a/__tests__/html2/activity/programmingLanguageTitle.html b/__tests__/html2/activity/programmingLanguageTitle.html new file mode 100644 index 0000000000..319a1d7ff4 --- /dev/null +++ b/__tests__/html2/activity/programmingLanguageTitle.html @@ -0,0 +1,176 @@ + + + + Code: shows the title of the programming language + + + + + + + + +
+ + + diff --git a/__tests__/html2/activity/programmingLanguageTitle.html.snap-1.png b/__tests__/html2/activity/programmingLanguageTitle.html.snap-1.png new file mode 100644 index 0000000000..3884dfbda2 Binary files /dev/null and b/__tests__/html2/activity/programmingLanguageTitle.html.snap-1.png differ diff --git a/__tests__/html2/activity/programmingLanguageTitle.html.snap-2.png b/__tests__/html2/activity/programmingLanguageTitle.html.snap-2.png new file mode 100644 index 0000000000..1b22b174cc Binary files /dev/null and b/__tests__/html2/activity/programmingLanguageTitle.html.snap-2.png differ diff --git a/__tests__/html2/markdown/codeBlock/contrast.code-block-dark.fluent.dark.html.snap-1.png b/__tests__/html2/markdown/codeBlock/contrast.code-block-dark.fluent.dark.html.snap-1.png index 6934e20a48..80a8010005 100644 Binary files a/__tests__/html2/markdown/codeBlock/contrast.code-block-dark.fluent.dark.html.snap-1.png and b/__tests__/html2/markdown/codeBlock/contrast.code-block-dark.fluent.dark.html.snap-1.png differ diff --git a/__tests__/html2/markdown/codeBlock/contrast.code-block-dark.fluent.dark.html.snap-2.png b/__tests__/html2/markdown/codeBlock/contrast.code-block-dark.fluent.dark.html.snap-2.png index 1414092bc7..25c978252f 100644 Binary files a/__tests__/html2/markdown/codeBlock/contrast.code-block-dark.fluent.dark.html.snap-2.png and b/__tests__/html2/markdown/codeBlock/contrast.code-block-dark.fluent.dark.html.snap-2.png differ diff --git a/__tests__/html2/markdown/codeBlock/contrast.code-block-dark.fluent.html.snap-1.png b/__tests__/html2/markdown/codeBlock/contrast.code-block-dark.fluent.html.snap-1.png index c7a676a2ee..f19177732b 100644 Binary files a/__tests__/html2/markdown/codeBlock/contrast.code-block-dark.fluent.html.snap-1.png and b/__tests__/html2/markdown/codeBlock/contrast.code-block-dark.fluent.html.snap-1.png differ diff --git a/__tests__/html2/markdown/codeBlock/contrast.code-block-dark.fluent.html.snap-2.png b/__tests__/html2/markdown/codeBlock/contrast.code-block-dark.fluent.html.snap-2.png index 1dc87f3b36..d22ef10aab 100644 Binary files a/__tests__/html2/markdown/codeBlock/contrast.code-block-dark.fluent.html.snap-2.png and b/__tests__/html2/markdown/codeBlock/contrast.code-block-dark.fluent.html.snap-2.png differ diff --git a/__tests__/html2/markdown/codeBlock/contrast.code-block-dark.html.snap-1.png b/__tests__/html2/markdown/codeBlock/contrast.code-block-dark.html.snap-1.png index 8f06a37354..c729fa5d81 100644 Binary files a/__tests__/html2/markdown/codeBlock/contrast.code-block-dark.html.snap-1.png and b/__tests__/html2/markdown/codeBlock/contrast.code-block-dark.html.snap-1.png differ diff --git a/__tests__/html2/markdown/codeBlock/contrast.code-block-dark.html.snap-2.png b/__tests__/html2/markdown/codeBlock/contrast.code-block-dark.html.snap-2.png index 61963eb72d..41033a13c8 100644 Binary files a/__tests__/html2/markdown/codeBlock/contrast.code-block-dark.html.snap-2.png and b/__tests__/html2/markdown/codeBlock/contrast.code-block-dark.html.snap-2.png differ diff --git a/__tests__/html2/markdown/codeBlock/contrast.fluent.dark.html.snap-1.png b/__tests__/html2/markdown/codeBlock/contrast.fluent.dark.html.snap-1.png index 5adc3461c2..5a641a1fdd 100644 Binary files a/__tests__/html2/markdown/codeBlock/contrast.fluent.dark.html.snap-1.png and b/__tests__/html2/markdown/codeBlock/contrast.fluent.dark.html.snap-1.png differ diff --git a/__tests__/html2/markdown/codeBlock/contrast.fluent.dark.html.snap-2.png b/__tests__/html2/markdown/codeBlock/contrast.fluent.dark.html.snap-2.png index 4655516095..971b9c8a52 100644 Binary files a/__tests__/html2/markdown/codeBlock/contrast.fluent.dark.html.snap-2.png and b/__tests__/html2/markdown/codeBlock/contrast.fluent.dark.html.snap-2.png differ diff --git a/__tests__/html2/markdown/codeBlock/contrast.fluent.dark.html.snap-3.png b/__tests__/html2/markdown/codeBlock/contrast.fluent.dark.html.snap-3.png index 5a0b50533d..f400c82d20 100644 Binary files a/__tests__/html2/markdown/codeBlock/contrast.fluent.dark.html.snap-3.png and b/__tests__/html2/markdown/codeBlock/contrast.fluent.dark.html.snap-3.png differ diff --git a/__tests__/html2/markdown/codeBlock/contrast.fluent.html.snap-1.png b/__tests__/html2/markdown/codeBlock/contrast.fluent.html.snap-1.png index 166aff0d0a..4de0cf19bc 100644 Binary files a/__tests__/html2/markdown/codeBlock/contrast.fluent.html.snap-1.png and b/__tests__/html2/markdown/codeBlock/contrast.fluent.html.snap-1.png differ diff --git a/__tests__/html2/markdown/codeBlock/contrast.fluent.html.snap-2.png b/__tests__/html2/markdown/codeBlock/contrast.fluent.html.snap-2.png index db19e4ec8d..19a97de596 100644 Binary files a/__tests__/html2/markdown/codeBlock/contrast.fluent.html.snap-2.png and b/__tests__/html2/markdown/codeBlock/contrast.fluent.html.snap-2.png differ diff --git a/__tests__/html2/markdown/codeBlock/contrast.fluent.html.snap-3.png b/__tests__/html2/markdown/codeBlock/contrast.fluent.html.snap-3.png index 320d714ac7..ed9209710c 100644 Binary files a/__tests__/html2/markdown/codeBlock/contrast.fluent.html.snap-3.png and b/__tests__/html2/markdown/codeBlock/contrast.fluent.html.snap-3.png differ diff --git a/__tests__/html2/markdown/codeBlock/contrast.html b/__tests__/html2/markdown/codeBlock/contrast.html index 6cab33789c..fbbb814051 100644 --- a/__tests__/html2/markdown/codeBlock/contrast.html +++ b/__tests__/html2/markdown/codeBlock/contrast.html @@ -58,7 +58,8 @@ return text.match(pattern) ?? null; } -}` +}`, + title: 'JavaScript' }), Object.freeze({ programmingLanguage: 'typescript', @@ -67,7 +68,8 @@ export function area(shape: Shape): number { return Math.PI * shape.radius ** 2; /* Block comment. */ -}` +}`, + title: 'TypeScript' }), Object.freeze({ programmingLanguage: 'python', @@ -81,7 +83,8 @@ def distance(self, other: "Point") -> float: return math.sqrt((self.x - other.x) ** 2) # Trailing comment. -print(f"{Point(1)!r}", r"\\d+", None, True)` +print(f"{Point(1)!r}", r"\\d+", None, True)`, + title: 'Python' }) ]); @@ -140,13 +143,13 @@ // The accessibility check cannot measure the color contrast of text in the transcript: the transcript focus overlays cover it. // The "View code" dialog shows the same highlighted code outside of the transcript. - for (const [index, { programmingLanguage }] of SAMPLES.entries()) { + for (const [index, { title }] of SAMPLES.entries()) { // WHEN: Click the "View code" button of the message. await host.click(document.querySelectorAll(`[data-testid="${testIds.viewCodeButton}"]`)[index]); // THEN: The "View code" dialog should show the code highlighted. await waitFor(() => expect(document.querySelector('dialog[open] pre.shiki')).toBeTruthy()); - expect(document.querySelector('dialog[open] h2').textContent).toBe(programmingLanguage); + expect(document.querySelector('dialog[open] h2').textContent).toBe(title); // THEN: Should match snapshot, and every token should pass the default accessibility check, including color contrast. await host.snapshot('local'); diff --git a/__tests__/html2/markdown/codeBlock/contrast.html.snap-1.png b/__tests__/html2/markdown/codeBlock/contrast.html.snap-1.png index 9036607021..e5e43ac18a 100644 Binary files a/__tests__/html2/markdown/codeBlock/contrast.html.snap-1.png and b/__tests__/html2/markdown/codeBlock/contrast.html.snap-1.png differ diff --git a/__tests__/html2/markdown/codeBlock/contrast.html.snap-2.png b/__tests__/html2/markdown/codeBlock/contrast.html.snap-2.png index e7f7ec32d3..b8f29f9785 100644 Binary files a/__tests__/html2/markdown/codeBlock/contrast.html.snap-2.png and b/__tests__/html2/markdown/codeBlock/contrast.html.snap-2.png differ diff --git a/__tests__/html2/markdown/codeBlock/contrast.html.snap-3.png b/__tests__/html2/markdown/codeBlock/contrast.html.snap-3.png index e7a06a724a..853c4297e4 100644 Binary files a/__tests__/html2/markdown/codeBlock/contrast.html.snap-3.png and b/__tests__/html2/markdown/codeBlock/contrast.html.snap-3.png differ diff --git a/__tests__/html2/part-grouping/copilot.dark.html.snap-8.png b/__tests__/html2/part-grouping/copilot.dark.html.snap-8.png index eef13eea0a..138a70425a 100644 Binary files a/__tests__/html2/part-grouping/copilot.dark.html.snap-8.png and b/__tests__/html2/part-grouping/copilot.dark.html.snap-8.png differ diff --git a/__tests__/html2/part-grouping/copilot.dark.html.snap-9.png b/__tests__/html2/part-grouping/copilot.dark.html.snap-9.png index 466006a618..8affc59e99 100644 Binary files a/__tests__/html2/part-grouping/copilot.dark.html.snap-9.png and b/__tests__/html2/part-grouping/copilot.dark.html.snap-9.png differ diff --git a/__tests__/html2/part-grouping/copilot.html.snap-8.png b/__tests__/html2/part-grouping/copilot.html.snap-8.png index 879cc2d273..7f7e467387 100644 Binary files a/__tests__/html2/part-grouping/copilot.html.snap-8.png and b/__tests__/html2/part-grouping/copilot.html.snap-8.png differ diff --git a/__tests__/html2/part-grouping/copilot.html.snap-9.png b/__tests__/html2/part-grouping/copilot.html.snap-9.png index 10596b3ef8..df6205b21c 100644 Binary files a/__tests__/html2/part-grouping/copilot.html.snap-9.png and b/__tests__/html2/part-grouping/copilot.html.snap-9.png differ diff --git a/__tests__/html2/part-grouping/fluent.dark.html.snap-8.png b/__tests__/html2/part-grouping/fluent.dark.html.snap-8.png index fd633b523b..7ba8c24414 100644 Binary files a/__tests__/html2/part-grouping/fluent.dark.html.snap-8.png and b/__tests__/html2/part-grouping/fluent.dark.html.snap-8.png differ diff --git a/__tests__/html2/part-grouping/fluent.dark.html.snap-9.png b/__tests__/html2/part-grouping/fluent.dark.html.snap-9.png index 47627d00fc..706c4017ee 100644 Binary files a/__tests__/html2/part-grouping/fluent.dark.html.snap-9.png and b/__tests__/html2/part-grouping/fluent.dark.html.snap-9.png differ diff --git a/__tests__/html2/part-grouping/fluent.html.snap-8.png b/__tests__/html2/part-grouping/fluent.html.snap-8.png index ec261d0fe7..2b7cce2599 100644 Binary files a/__tests__/html2/part-grouping/fluent.html.snap-8.png and b/__tests__/html2/part-grouping/fluent.html.snap-8.png differ diff --git a/__tests__/html2/part-grouping/fluent.html.snap-9.png b/__tests__/html2/part-grouping/fluent.html.snap-9.png index cf3db90bb4..c0378e1a91 100644 Binary files a/__tests__/html2/part-grouping/fluent.html.snap-9.png and b/__tests__/html2/part-grouping/fluent.html.snap-9.png differ diff --git a/packages/api/src/localization/en-US.json b/packages/api/src/localization/en-US.json index 7e22ba4c18..3025d22f37 100644 --- a/packages/api/src/localization/en-US.json +++ b/packages/api/src/localization/en-US.json @@ -95,6 +95,46 @@ "_FILE_CONTENT_ALT.comment": "$1 is the name of the file, e.g. document.docx. $2 is the file size, e.g. 12 MB", "MARKDOWN_EXTERNAL_LINK_ALT": "Opens in a new window; external.", "_MARKDOWN_EXTERNAL_LINK_ALT.comment": "This is for the screen reader and is added to the label of the hyperlink. When clicked, the hyperlink will open in a new tab or window.", + "PROGRAMMING_LANGUAGE_CPP": "C++", + "_PROGRAMMING_LANGUAGE_CPP.comment": "This is the name of a programming language, shown as the title of a code sample. Translate only if the name differs in your language.", + "PROGRAMMING_LANGUAGE_CSHARP": "C#", + "_PROGRAMMING_LANGUAGE_CSHARP.comment": "This is the name of a programming language, shown as the title of a code sample. Translate only if the name differs in your language.", + "PROGRAMMING_LANGUAGE_CSS": "CSS", + "_PROGRAMMING_LANGUAGE_CSS.comment": "This is the name of a programming language, shown as the title of a code sample. Translate only if the name differs in your language.", + "PROGRAMMING_LANGUAGE_HTML": "HTML", + "_PROGRAMMING_LANGUAGE_HTML.comment": "This is the name of a programming language, shown as the title of a code sample. Translate only if the name differs in your language.", + "PROGRAMMING_LANGUAGE_JAVASCRIPT": "JavaScript", + "_PROGRAMMING_LANGUAGE_JAVASCRIPT.comment": "This is the name of a programming language, shown as the title of a code sample. Translate only if the name differs in your language.", + "PROGRAMMING_LANGUAGE_JSON": "JSON", + "_PROGRAMMING_LANGUAGE_JSON.comment": "This is the name of a programming language, shown as the title of a code sample. Translate only if the name differs in your language.", + "PROGRAMMING_LANGUAGE_JSX": "JSX", + "_PROGRAMMING_LANGUAGE_JSX.comment": "This is the name of a programming language, shown as the title of a code sample. Translate only if the name differs in your language.", + "PROGRAMMING_LANGUAGE_KOTLIN": "Kotlin", + "_PROGRAMMING_LANGUAGE_KOTLIN.comment": "This is the name of a programming language, shown as the title of a code sample. Translate only if the name differs in your language.", + "PROGRAMMING_LANGUAGE_MARKDOWN": "Markdown", + "_PROGRAMMING_LANGUAGE_MARKDOWN.comment": "This is the name of a programming language, shown as the title of a code sample. Translate only if the name differs in your language.", + "PROGRAMMING_LANGUAGE_PHP": "PHP", + "_PROGRAMMING_LANGUAGE_PHP.comment": "This is the name of a programming language, shown as the title of a code sample. Translate only if the name differs in your language.", + "PROGRAMMING_LANGUAGE_POWERSHELL": "PowerShell", + "_PROGRAMMING_LANGUAGE_POWERSHELL.comment": "This is the name of a programming language, shown as the title of a code sample. Translate only if the name differs in your language.", + "PROGRAMMING_LANGUAGE_PYTHON": "Python", + "_PROGRAMMING_LANGUAGE_PYTHON.comment": "This is the name of a programming language, shown as the title of a code sample. Translate only if the name differs in your language.", + "PROGRAMMING_LANGUAGE_RUBY": "Ruby", + "_PROGRAMMING_LANGUAGE_RUBY.comment": "This is the name of a programming language, shown as the title of a code sample. Translate only if the name differs in your language.", + "PROGRAMMING_LANGUAGE_RUST": "Rust", + "_PROGRAMMING_LANGUAGE_RUST.comment": "This is the name of a programming language, shown as the title of a code sample. Translate only if the name differs in your language.", + "PROGRAMMING_LANGUAGE_SHELL": "Shell", + "_PROGRAMMING_LANGUAGE_SHELL.comment": "This is the name of a programming language, shown as the title of a code sample. Translate only if the name differs in your language.", + "PROGRAMMING_LANGUAGE_SQL": "SQL", + "_PROGRAMMING_LANGUAGE_SQL.comment": "This is the name of a programming language, shown as the title of a code sample. Translate only if the name differs in your language.", + "PROGRAMMING_LANGUAGE_TSX": "TSX", + "_PROGRAMMING_LANGUAGE_TSX.comment": "This is the name of a programming language, shown as the title of a code sample. Translate only if the name differs in your language.", + "PROGRAMMING_LANGUAGE_TYPESCRIPT": "TypeScript", + "_PROGRAMMING_LANGUAGE_TYPESCRIPT.comment": "This is the name of a programming language, shown as the title of a code sample. Translate only if the name differs in your language.", + "PROGRAMMING_LANGUAGE_XML": "XML", + "_PROGRAMMING_LANGUAGE_XML.comment": "This is the name of a programming language, shown as the title of a code sample. Translate only if the name differs in your language.", + "PROGRAMMING_LANGUAGE_YAML": "YAML", + "_PROGRAMMING_LANGUAGE_YAML.comment": "This is the name of a programming language, shown as the title of a code sample. Translate only if the name differs in your language.", "RECEIPT_CARD_TAX": "Tax", "RECEIPT_CARD_TOTAL": "Total", "RECEIPT_CARD_VAT": "VAT", diff --git a/packages/bundle/src/codeHighlighter/ShikiComposer.tsx b/packages/bundle/src/codeHighlighter/ShikiComposer.tsx index ad0bb7fb86..a37b9f72b7 100644 --- a/packages/bundle/src/codeHighlighter/ShikiComposer.tsx +++ b/packages/bundle/src/codeHighlighter/ShikiComposer.tsx @@ -8,7 +8,8 @@ const createHighligtCodeWithShiki = (shikiHiglighter: HighlighterCore): HighlightCodeFn => (code, language, options) => shikiHiglighter.codeToHtml(code, { - lang: language, + // Shiki resolves languages and aliases in lowercase only, e.g. "ts", while bots may send "TS". + lang: language?.toLowerCase(), theme: options.theme }); diff --git a/packages/component/src/Activity/CodeBlockContent.module.css b/packages/component/src/Activity/CodeBlockContent.module.css index 28735a05a2..61b0fb2998 100644 --- a/packages/component/src/Activity/CodeBlockContent.module.css +++ b/packages/component/src/Activity/CodeBlockContent.module.css @@ -25,10 +25,6 @@ :global(.webchat) .code-block-content__title { font-weight: 600; - - &::first-letter { - text-transform: uppercase; - } } :global(.webchat) .code-block-content .code-block-content__code-block { diff --git a/packages/component/src/Activity/StackedLayout.tsx b/packages/component/src/Activity/StackedLayout.tsx index a2f8f533d4..419980ae39 100644 --- a/packages/component/src/Activity/StackedLayout.tsx +++ b/packages/component/src/Activity/StackedLayout.tsx @@ -19,6 +19,7 @@ import ScreenReaderText from '../ScreenReaderText'; import isZeroOrPositive from '../Utils/isZeroOrPositive'; import getFirstBaseOfSoftwareSourceCode from '../Utils/orgSchema/getFirstBaseOfSoftwareSourceCode'; import textFormatToContentType from '../Utils/textFormatToContentType'; +import useLocalizeProgrammingLanguage from '../hooks/internal/useLocalizeProgrammingLanguage'; import { useGetLogicalGroupKey } from '../providers/ActivityLogicalGrouping'; import AttachmentRow from './AttachmentRow'; import Bubble from './Bubble'; @@ -113,6 +114,7 @@ const StackedLayout = ({ const [{ initials: botInitials }] = useAvatarForBot(); const [{ initials: userInitials }] = useAvatarForUser(); const localize = useLocalizer(); + const localizeProgrammingLanguage = useLocalizeProgrammingLanguage(); const classNames = useStyles(styles); const { bubbleNubOffset, bubbleNubSize, bubbleFromUserNubOffset, bubbleFromUserNubSize } = styleOptions; @@ -214,7 +216,7 @@ const StackedLayout = ({ code={firstSoftwareSourceCode.text[0]} key={syntheticAttachments.length} language={firstSoftwareSourceCode.programmingLanguage[0]} - title={firstSoftwareSourceCode.programmingLanguage[0]} + title={localizeProgrammingLanguage(firstSoftwareSourceCode.programmingLanguage[0])} /> ); @@ -244,6 +246,7 @@ const StackedLayout = ({ hasNub, isCollapsible, localize, + localizeProgrammingLanguage, messageThing, otherInitials, renderAttachment, diff --git a/packages/component/src/Attachment/Text/private/MarkdownTextContent.tsx b/packages/component/src/Attachment/Text/private/MarkdownTextContent.tsx index b95e354abf..70e25fe7a9 100644 --- a/packages/component/src/Attachment/Text/private/MarkdownTextContent.tsx +++ b/packages/component/src/Attachment/Text/private/MarkdownTextContent.tsx @@ -17,6 +17,7 @@ import ActivityFeedback from '../../../ActivityFeedback/ActivityFeedback'; import { LinkDefinitionItem, LinkDefinitions } from '../../../LinkDefinition/index'; import dereferenceBlankNodes from '../../../Utils/JSONLinkedData/dereferenceBlankNodes'; import getFirstBaseOfSoftwareSourceCode from '../../../Utils/orgSchema/getFirstBaseOfSoftwareSourceCode'; +import useLocalizeProgrammingLanguage from '../../../hooks/internal/useLocalizeProgrammingLanguage'; import useSanitizeHrefCallback from '../../../hooks/internal/useSanitizeHrefCallback'; import useStreamingMarkdownWithDefinitions, { type MarkdownLinkDefinition @@ -82,6 +83,7 @@ function MarkdownTextContent(props: MarkdownTextContentProps) { const [{ feedbackActionsPlacement }] = useStyleOptions(); const contentRef = useRef(null); const localize = useLocalizer(); + const localizeProgrammingLanguage = useLocalizeProgrammingLanguage(); const graph = useMemo(() => dereferenceBlankNodes(activity.entities || []), [activity.entities]); const showModal = useShowModal(); @@ -311,7 +313,7 @@ function MarkdownTextContent(props: MarkdownTextContentProps) { code={firstSoftwareSourceCodeBase.text[0]} isAIGenerated={isAIGeneratedActivity(activity)} language={firstSoftwareSourceCodeBase.programmingLanguage[0]} - title={firstSoftwareSourceCodeBase.programmingLanguage[0]} + title={localizeProgrammingLanguage(firstSoftwareSourceCodeBase.programmingLanguage[0])} /> ) : null} {activity.type === 'message' && activity.text && messageThing?.keywords.includes('AllowCopy') ? ( diff --git a/packages/component/src/Attachment/Text/private/ViewCodeDialog.module.css b/packages/component/src/Attachment/Text/private/ViewCodeDialog.module.css index caa970d43c..10dc6c50e4 100644 --- a/packages/component/src/Attachment/Text/private/ViewCodeDialog.module.css +++ b/packages/component/src/Attachment/Text/private/ViewCodeDialog.module.css @@ -36,7 +36,6 @@ :global(.webchat) .view-code-dialog__title { margin: 0 auto 0 0; - text-transform: capitalize; } :global(.webchat) .view-code-dialog .view-code-dialog__body { diff --git a/packages/component/src/hooks/internal/useLocalizeProgrammingLanguage.ts b/packages/component/src/hooks/internal/useLocalizeProgrammingLanguage.ts new file mode 100644 index 0000000000..eb79461a5c --- /dev/null +++ b/packages/component/src/hooks/internal/useLocalizeProgrammingLanguage.ts @@ -0,0 +1,53 @@ +import { hooks } from 'botframework-webchat-api'; +import { useCallback } from 'react'; + +const { useLocalizer } = hooks; + +// Names and aliases (in lowercase) follow Shiki ("bundledLanguagesInfo" in "shiki/langs"), except that shell dialects, +// such as "bash" and "zsh", keep their own name instead of "Shell". +// Languages without aliases and titled with their capitalized ID, such as "go" ("Go"), need no string. +const STRING_ID_BY_PROGRAMMING_LANGUAGE: ReadonlyMap = Object.freeze( + new Map( + Object.entries({ + PROGRAMMING_LANGUAGE_CPP: ['c++', 'cpp'], + PROGRAMMING_LANGUAGE_CSHARP: ['c#', 'cs', 'csharp'], + PROGRAMMING_LANGUAGE_CSS: ['css'], + PROGRAMMING_LANGUAGE_HTML: ['html'], + PROGRAMMING_LANGUAGE_JAVASCRIPT: ['javascript', 'js'], + PROGRAMMING_LANGUAGE_JSON: ['json'], + PROGRAMMING_LANGUAGE_JSX: ['jsx'], + PROGRAMMING_LANGUAGE_KOTLIN: ['kotlin', 'kt', 'kts'], + PROGRAMMING_LANGUAGE_MARKDOWN: ['markdown', 'md'], + PROGRAMMING_LANGUAGE_PHP: ['php'], + PROGRAMMING_LANGUAGE_POWERSHELL: ['powershell', 'ps', 'ps1'], + PROGRAMMING_LANGUAGE_PYTHON: ['py', 'python'], + PROGRAMMING_LANGUAGE_RUBY: ['rb', 'ruby'], + PROGRAMMING_LANGUAGE_RUST: ['rs', 'rust'], + PROGRAMMING_LANGUAGE_SHELL: ['sh', 'shell', 'shellscript'], + PROGRAMMING_LANGUAGE_SQL: ['sql'], + PROGRAMMING_LANGUAGE_TSX: ['tsx'], + PROGRAMMING_LANGUAGE_TYPESCRIPT: ['ts', 'typescript'], + PROGRAMMING_LANGUAGE_XML: ['xml'], + PROGRAMMING_LANGUAGE_YAML: ['yaml', 'yml'] + }).flatMap(([stringId, aliases]) => aliases.map(alias => [alias, stringId] as const)) + ) +); + +/** + * Returns a function to get the title of a programming language, e.g. "JavaScript" for "javascript" or "js". + * + * Titles of known programming languages are localizable strings, which can be overridden via `overrideLocalizedStrings`. + * For other programming languages, the first letter is capitalized. + */ +export default function useLocalizeProgrammingLanguage(): (language: string | undefined) => string | undefined { + const localize = useLocalizer(); + + return useCallback( + (language: string | undefined) => { + const stringId = language && STRING_ID_BY_PROGRAMMING_LANGUAGE.get(language.toLowerCase()); + + return stringId ? localize(stringId) : language && language.charAt(0).toUpperCase() + language.slice(1); + }, + [localize] + ); +}