feat(translation): Improve translation alert in empty languages state (#14464)

**What**
When there is no languages configured, the translation page will show a proper alert on the page that is more visible and actionable
This commit is contained in:
Adrien de Peretti
2026-01-06 16:04:23 +01:00
committed by GitHub
parent b300a93345
commit d54589751c
7 changed files with 71 additions and 38 deletions

View File

@@ -0,0 +1,6 @@
---
"@medusajs/dashboard": patch
"@medusajs/ui": patch
---
feat(translation): Improve translation alert in empty languages state

View File

@@ -9444,9 +9444,17 @@
}, },
"noLocalesTip": { "noLocalesTip": {
"type": "string" "type": "string"
},
"noLocalesTipConfigureAction": {
"type": "string"
} }
}, },
"required": ["heading", "subtitle", "noLocalesTip"] "required": [
"heading",
"subtitle",
"noLocalesTip",
"noLocalesTipConfigureAction"
]
}, },
"completion": { "completion": {
"type": "object", "type": "object",

View File

@@ -2540,7 +2540,8 @@
"activeLocales": { "activeLocales": {
"heading": "Languages", "heading": "Languages",
"subtitle": "Activated translations", "subtitle": "Activated translations",
"noLocalesTip": "Configure at least one locale to start translating your data" "noLocalesTip": "Configure at least one language to start translating your data",
"noLocalesTipConfigureAction": "Configure"
}, },
"completion": { "completion": {
"heading": "Translated fields", "heading": "Translated fields",

View File

@@ -2480,7 +2480,8 @@
"activeLocales": { "activeLocales": {
"heading": "Idiomas", "heading": "Idiomas",
"subtitle": "Traducciones activas", "subtitle": "Traducciones activas",
"noLocalesTip": "Configura al menos un idioma para empezar a traducir tu información" "noLocalesTip": "Configura al menos un lenguaje para empezar a traducir tu información",
"noLocalesTipConfigureAction": "Configurar"
}, },
"completion": { "completion": {
"heading": "Textos traducidos", "heading": "Textos traducidos",

View File

@@ -1,17 +1,10 @@
import { PencilSquare, Language } from "@medusajs/icons" import { Language } from "@medusajs/icons"
import { import { HttpTypes } from "@medusajs/types"
Container, import { Container, Heading, Text, Tooltip } from "@medusajs/ui"
Heading, import { useCallback, useState } from "react"
IconButton,
InlineTip,
Text,
Tooltip,
} from "@medusajs/ui"
import { useTranslation } from "react-i18next" import { useTranslation } from "react-i18next"
import { IconAvatar } from "../../../../../components/common/icon-avatar" import { IconAvatar } from "../../../../../components/common/icon-avatar"
import { HttpTypes } from "@medusajs/types" import { LinkButton } from "../../../../../components/common/link-button"
import { useCallback, useState } from "react"
import { useNavigate } from "react-router-dom"
type ActiveLocalesSectionProps = { type ActiveLocalesSectionProps = {
locales: HttpTypes.AdminLocale[] locales: HttpTypes.AdminLocale[]
@@ -21,13 +14,8 @@ export const ActiveLocalesSection = ({
locales, locales,
}: ActiveLocalesSectionProps) => { }: ActiveLocalesSectionProps) => {
const { t } = useTranslation() const { t } = useTranslation()
const navigate = useNavigate()
const [isHovered, setIsHovered] = useState(false) const [isHovered, setIsHovered] = useState(false)
const handleManageLocales = useCallback(() => {
navigate("/settings/translations/add-locales")
}, [navigate])
const renderLocales = useCallback(() => { const renderLocales = useCallback(() => {
const maxLocalesToDetail = 2 const maxLocalesToDetail = 2
if (locales.length <= maxLocalesToDetail) { if (locales.length <= maxLocalesToDetail) {
@@ -46,18 +34,26 @@ export const ActiveLocalesSection = ({
<Container className="flex flex-col p-0"> <Container className="flex flex-col p-0">
<div className="flex items-center justify-between px-6 py-4"> <div className="flex items-center justify-between px-6 py-4">
<Heading level="h2">{t("translations.activeLocales.heading")}</Heading> <Heading level="h2">{t("translations.activeLocales.heading")}</Heading>
<IconButton variant="transparent" onClick={handleManageLocales}> <LinkButton
<PencilSquare></PencilSquare> variant="interactive"
</IconButton> className="text-ui-fg-subtle hover:text-ui-fg-subtle-hover"
to="/settings/translations/add-locales"
>
{t("translations.activeLocales.noLocalesTipConfigureAction")}
</LinkButton>
</div> </div>
<div className="px-1 pb-1"> {hasLocales && (
{hasLocales ? ( <div className="px-1 pb-1">
<Tooltip <Tooltip
open={isHovered} open={isHovered}
content={ content={
<div className="flex flex-col gap-y-1 p-1"> <div className="flex flex-col gap-y-1 p-1">
{locales.map((locale) => ( {locales.map((locale) => (
<Text key={locale.code} size="small" weight="plus"> <Text
key={locale.code}
size="base"
className="text-ui-fg-subtle"
>
{locale.name} {locale.name}
</Text> </Text>
))} ))}
@@ -82,12 +78,8 @@ export const ActiveLocalesSection = ({
</div> </div>
</Container> </Container>
</Tooltip> </Tooltip>
) : ( </div>
<InlineTip label="Tip"> )}
{t("translations.activeLocales.noLocalesTip")}
</InlineTip>
)}
</div>
</Container> </Container>
) )
} }

View File

@@ -1,6 +1,9 @@
import { Container, Heading, Text } from "@medusajs/ui" import { Alert, Button, Container, Heading, Text } from "@medusajs/ui"
import { TwoColumnPage } from "../../../components/layout/pages" import { useCallback, useMemo } from "react"
import { useTranslation } from "react-i18next" import { useTranslation } from "react-i18next"
import { useNavigate } from "react-router-dom"
import { TwoColumnPageSkeleton } from "../../../components/common/skeleton"
import { TwoColumnPage } from "../../../components/layout/pages"
import { import {
useStore, useStore,
useTranslationSettings, useTranslationSettings,
@@ -9,8 +12,6 @@ import {
import { ActiveLocalesSection } from "./components/active-locales-section/active-locales-section" import { ActiveLocalesSection } from "./components/active-locales-section/active-locales-section"
import { TranslationListSection } from "./components/translation-list-section/translation-list-section" import { TranslationListSection } from "./components/translation-list-section/translation-list-section"
import { TranslationsCompletionSection } from "./components/translations-completion-section/translations-completion-section" import { TranslationsCompletionSection } from "./components/translations-completion-section/translations-completion-section"
import { TwoColumnPageSkeleton } from "../../../components/common/skeleton"
import { useMemo } from "react"
export type TranslatableEntity = { export type TranslatableEntity = {
label: string label: string
@@ -22,6 +23,7 @@ export type TranslatableEntity = {
export const TranslationList = () => { export const TranslationList = () => {
const { t } = useTranslation() const { t } = useTranslation()
const navigate = useNavigate()
const { store, isPending, isError, error } = useStore() const { store, isPending, isError, error } = useStore()
const { const {
@@ -88,6 +90,10 @@ export const TranslationList = () => {
) )
}, [translatable_fields, statistics]) }, [translatable_fields, statistics])
const handleManageLocales = useCallback(() => {
navigate("/settings/translations/add-locales")
}, [navigate])
const isReady = const isReady =
!!store && !!store &&
!isPending && !isPending &&
@@ -115,6 +121,25 @@ export const TranslationList = () => {
{t("translations.subtitle")} {t("translations.subtitle")}
</Text> </Text>
</Container> </Container>
{!hasLocales && (
<Alert
variant="info"
className="bg-ui-bg-base flex items-center px-6 py-4"
>
<div className="flex items-center justify-between gap-x-2">
<p>{t("translations.activeLocales.noLocalesTip")}.</p>
<Button
onClick={handleManageLocales}
size="small"
variant="secondary"
>
{t("translations.activeLocales.noLocalesTipConfigureAction")}
</Button>
</div>
</Alert>
)}
<TranslationListSection <TranslationListSection
entities={translatableEntities} entities={translatableEntities}
hasLocales={hasLocales} hasLocales={hasLocales}

View File

@@ -60,7 +60,7 @@ export const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
<div <div
ref={ref} ref={ref}
className={clx( className={clx(
"bg-ui-bg-subtle text-pretty txt-compact-small grid items-start gap-x-2 rounded-lg border p-3", "bg-ui-bg-subtle txt-compact-small grid items-start gap-x-2 text-pretty rounded-lg border p-3",
{ {
"grid-cols-[20px_1fr]": !dismissible, "grid-cols-[20px_1fr]": !dismissible,
"grid-cols-[20px_1fr_20px]": dismissible, "grid-cols-[20px_1fr_20px]": dismissible,
@@ -77,7 +77,7 @@ export const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
"text-ui-tag-neutral-icon": variant === "info", "text-ui-tag-neutral-icon": variant === "info",
})} })}
/> />
<div>{children}</div> <div className="w-full">{children}</div>
{dismissible && ( {dismissible && (
<IconButton <IconButton
size="2xsmall" size="2xsmall"