Files
twenty/packages/twenty-website/src/sections/Problem/ProblemPoints.tsx
T
Abdullah. b027e4bdb1 [Website] i18n module, page-local sections, translatable copy (#21082)
**i18n** — collapsed the ~22 scattered i18n files into a single module
and turned on Spanish alongside French.

**Sections** — dropped the old compound pattern (`Section.Root`,
`Section.Heading`, …). Reusable layout shells moved to `src/templates/`,
atomic bits stay in `design-system/`, and each page now owns its copy in
local `_components` blocks instead of pulling it out of shared sections.
Data files hold arrays only, no prose.

**Copy → `<Trans>`** — A lot of headings were split across several
`<HeadingPart>`s just for font styling, which meant each piece was a
separate translation string. A translator got "Build your Enterprise
CRM" and "at AI Speed" as two unrelated strings and had no way to
reorder them for their language. Those are now single `<Trans>` units
with placeholders. Same idea for the old `\n` + `white-space: pre-line`
line-break trick: replaced with a small `ResponsiveLineBreak` element so
the break is doesn't quietly rot, and did a dead-code pass.

The de-fragmentation changes the message IDs, so around 60 strings will
fall back to English in fr/es until Crowdin re-syncs.
2026-05-31 12:39:35 +00:00

57 lines
1.3 KiB
TypeScript

import { Body } from '@/design-system/components/Body';
import { getServerI18n } from '@/lib/i18n/server';
import { theme } from '@/theme';
import { styled } from '@linaria/react';
import React from 'react';
import type { ProblemPointType } from './problem-point-type';
const StyledPoints = styled.div`
display: grid;
grid-template-columns: 1fr;
row-gap: ${theme.spacing(6)};
@media (min-width: ${theme.breakpoints.md}px) {
max-width: 454px;
}
`;
const StyledDivider = styled.div`
height: 0;
border-top: 0.75px dashed ${theme.colors.primary.border[80]};
width: 100%;
opacity: 50%;
`;
const StyledPoint = styled.div`
display: grid;
grid-template-columns: 1fr;
row-gap: ${theme.spacing(2)};
@media (min-width: ${theme.breakpoints.md}px) {
max-width: 380px;
width: 100%;
}
`;
type PointsProps = {
points: ProblemPointType[];
};
export function Points({ points }: PointsProps) {
const i18n = getServerI18n();
return (
<StyledPoints>
{points.map((point, index) => (
<React.Fragment key={index}>
<StyledDivider />
<StyledPoint>
{point.heading}
<Body size="sm">{i18n._(point.body)}</Body>
</StyledPoint>
</React.Fragment>
))}
</StyledPoints>
);
}