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; `} > - = 18} - lines={2} - > - {secondary} - + {secondaryLinesToRender.length > 0 && + (hasMultipleSecondaryLines ? ( + + {/* Clamp the first line so the tag line always stays visible. */} + + {secondaryPrimaryLine} + + {secondaryRemainingLines.map((line, index) => ( +
{line}
+ ))} +
+ ) : ( + + {secondaryPrimaryLine} + + ))}