diff --git a/.vscode/settings.json b/.vscode/settings.json
index 321a01ab..93956523 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,9 +1,12 @@
{
"workbench.colorCustomizations": {
- "statusBar.background": "#49d668",
- "statusBar.debuggingBackground": "#49d668",
+ "statusBar.background": "#549d3e",
"statusBar.noFolderBackground": "#49d668",
- "statussBar.prominentBackground": "#D65649"
+ "statussBar.prominentBackground": "#D65649",
+ "statusBar.foreground": "#e7e7e7",
+ "statusBarItem.hoverBackground": "#6bbb53",
+ "statusBarItem.remoteBackground": "#549d3e",
+ "statusBarItem.remoteForeground": "#e7e7e7"
},
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
@@ -29,5 +32,6 @@
"uilang",
"voca",
"yagni"
- ]
+ ],
+ "peacock.color": "#549d3e"
}
diff --git a/src/components/LanguageCard.tsx b/src/components/LanguageCard.tsx
index 9f333068..636c1ad5 100644
--- a/src/components/LanguageCard.tsx
+++ b/src/components/LanguageCard.tsx
@@ -2,7 +2,11 @@ import { css } from "@emotion/react";
import React from "react";
import { CheapCard } from "./CheapCard";
-import { ILanguage, getDisplayNamesForLanguage } from "../model/Language";
+import {
+ ILanguage,
+ getDisplayNamesForLanguage,
+ kTagForNoLanguage,
+} from "../model/Language";
import { commonUI } from "../theme";
import { useResponsiveChoice } from "../responsiveUtilities";
import { FormattedMessage } from "react-intl";
@@ -43,13 +47,55 @@ export const LanguageCard: React.FunctionComponent<
...propsToPassDown
} = props; // Prevent React warnings
- const { primary, secondary } = getDisplayNamesForLanguage(props);
+ const displayNames = getDisplayNamesForLanguage(props);
const getResponsiveChoice = useResponsiveChoice();
const { cardWidthPx, cardHeightPx } = useLanguageCardSpec(props.larger);
const urlPrefix = props.targetPrefix ?? "/language:";
const showCount = !useIsAppHosted();
const cardSpacing = useBaseCardSpec().cardSpacingPx;
+ const isPictureBook = isoCode === kTagForNoLanguage;
+
+ // BL-15812 Prefer the autonym (`name`) as the primary label; fall back to existing display logic
+ // for picture books or other special cases where `name` can be empty.
+ const primary = isPictureBook
+ ? displayNames.primary
+ : name.trim()
+ ? name
+ : displayNames.primary;
+
+ // Build the gray header labels explicitly so English and the tag can be separate lines.
+ const secondaryLinesToRender: string[] = [];
+ if (isPictureBook) {
+ // Preserve the historical duplication for picture-book cards.
+ secondaryLinesToRender.push(displayNames.primary);
+ } else {
+ if (englishName && englishName !== primary) {
+ secondaryLinesToRender.push(englishName);
+ }
+ // Match the tag as a whole token to avoid false positives like
+ // "fr" matching the autonym "français".
+ const secondaryTokens = displayNames.secondary
+ ? displayNames.secondary.split(/\s+/)
+ : [];
+ const shouldShowTag =
+ !!isoCode &&
+ secondaryTokens.includes(isoCode) &&
+ isoCode !== englishName &&
+ isoCode !== primary;
+ if (shouldShowTag) {
+ secondaryLinesToRender.push(isoCode);
+ }
+ }
+ const secondaryLineText = secondaryLinesToRender.join(" ");
+ const hasMultipleSecondaryLines = secondaryLinesToRender.length > 1;
+ // Only used for the single-line case, where we can allow two lines of wrap.
+ const shouldTruncateSecondary = secondaryLineText.length >= 18;
+ const [
+ secondaryPrimaryLine,
+ ...secondaryRemainingLines
+ ] = secondaryLinesToRender;
+
// In the main website, we want language cards to be responsive: smaller and with smaller text on small screens.
// In the language chooser intended to be embedded in BloomReader, we want larger sizes.
// The description said "about a third larger" which happens to be, for most measurements, what the large-screen
@@ -119,12 +165,25 @@ export const LanguageCard: React.FunctionComponent<
line-height: 1em;
`}
>
-