Translate portal chrome and page strings
Replace the remaining hard-coded UI copy in the top bar, user menu, home hero, and the documents/subprocessors/updates/requests pages with i18next keys, and supply the en-US and fr-FR catalog entries for them. The home hero title uses value interpolation rather than string concatenation so word order stays correct across locales. Signed-off-by: Émile Ré <emile@probo.com>
This commit is contained in:
@@ -1,5 +1,32 @@
|
|||||||
{
|
{
|
||||||
|
"topBar": {
|
||||||
|
"tagline": "Compliance Portal",
|
||||||
|
"getAccess": "Get Access",
|
||||||
|
"nav": {
|
||||||
|
"documents": "Documents",
|
||||||
|
"subprocessors": "Subprocessors",
|
||||||
|
"updates": "Updates",
|
||||||
|
"requests": "Requests"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"userMenu": {
|
||||||
|
"signOut": "Sign out"
|
||||||
|
},
|
||||||
|
"home": {
|
||||||
|
"heroTitle": "Trust at {{name}}."
|
||||||
|
},
|
||||||
"documents": {
|
"documents": {
|
||||||
"title": "Documents"
|
"title": "Documents"
|
||||||
|
},
|
||||||
|
"subprocessors": {
|
||||||
|
"title": "Subprocessors"
|
||||||
|
},
|
||||||
|
"updates": {
|
||||||
|
"title": "Updates",
|
||||||
|
"subscribe": "Subscribe to updates"
|
||||||
|
},
|
||||||
|
"requests": {
|
||||||
|
"title": "Data Requests",
|
||||||
|
"newRequest": "New Request"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,32 @@
|
|||||||
{
|
{
|
||||||
|
"topBar": {
|
||||||
|
"tagline": "Portail de conformité",
|
||||||
|
"getAccess": "Obtenir l'accès",
|
||||||
|
"nav": {
|
||||||
|
"documents": "Documents",
|
||||||
|
"subprocessors": "Sous-traitants",
|
||||||
|
"updates": "Mises à jour",
|
||||||
|
"requests": "Demandes"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"userMenu": {
|
||||||
|
"signOut": "Se déconnecter"
|
||||||
|
},
|
||||||
|
"home": {
|
||||||
|
"heroTitle": "La confiance chez {{name}}."
|
||||||
|
},
|
||||||
"documents": {
|
"documents": {
|
||||||
"title": "Documents"
|
"title": "Documents"
|
||||||
|
},
|
||||||
|
"subprocessors": {
|
||||||
|
"title": "Sous-traitants"
|
||||||
|
},
|
||||||
|
"updates": {
|
||||||
|
"title": "Mises à jour",
|
||||||
|
"subscribe": "S'abonner aux mises à jour"
|
||||||
|
},
|
||||||
|
"requests": {
|
||||||
|
"title": "Demandes de données",
|
||||||
|
"newRequest": "Nouvelle demande"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import { Avatar } from "@probo/ui/src/v2/Avatar/Avatar";
|
|||||||
import { Button } from "@probo/ui/src/v2/Button/Button";
|
import { Button } from "@probo/ui/src/v2/Button/Button";
|
||||||
import { Link } from "@probo/ui/src/v2/Button/Link";
|
import { Link } from "@probo/ui/src/v2/Button/Link";
|
||||||
import { Text } from "@probo/ui/src/v2/typography/Text";
|
import { Text } from "@probo/ui/src/v2/typography/Text";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { graphql, useFragment } from "react-relay";
|
import { graphql, useFragment } from "react-relay";
|
||||||
import { Link as RouterLink, useLocation } from "react-router";
|
import { Link as RouterLink, useLocation } from "react-router";
|
||||||
|
|
||||||
@@ -25,10 +26,10 @@ import { TopBarUserMenu } from "./TopBarUserMenu";
|
|||||||
import { topBar } from "./variants";
|
import { topBar } from "./variants";
|
||||||
|
|
||||||
const NAV_ITEMS = [
|
const NAV_ITEMS = [
|
||||||
{ to: "/documents", label: "Documents" },
|
{ to: "/documents", labelKey: "topBar.nav.documents" },
|
||||||
{ to: "/subprocessors", label: "Subprocessors" },
|
{ to: "/subprocessors", labelKey: "topBar.nav.subprocessors" },
|
||||||
{ to: "/updates", label: "Updates" },
|
{ to: "/updates", labelKey: "topBar.nav.updates" },
|
||||||
{ to: "/requests", label: "Requests" },
|
{ to: "/requests", labelKey: "topBar.nav.requests" },
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
const topBarFragment = graphql`
|
const topBarFragment = graphql`
|
||||||
@@ -57,6 +58,7 @@ function isActive(pathname: string, to: string): boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function TopBar({ queryKey }: TopBarProps) {
|
export function TopBar({ queryKey }: TopBarProps) {
|
||||||
|
const { t } = useTranslation();
|
||||||
const data = useFragment(topBarFragment, queryKey);
|
const data = useFragment(topBarFragment, queryKey);
|
||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
|
|
||||||
@@ -83,7 +85,7 @@ export function TopBar({ queryKey }: TopBarProps) {
|
|||||||
{organizationName}
|
{organizationName}
|
||||||
</Text>
|
</Text>
|
||||||
<Text size={2} color="neutral">
|
<Text size={2} color="neutral">
|
||||||
Compliance Portal
|
{t("topBar.tagline")}
|
||||||
</Text>
|
</Text>
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
|
|
||||||
@@ -99,13 +101,13 @@ export function TopBar({ queryKey }: TopBarProps) {
|
|||||||
size={2}
|
size={2}
|
||||||
active={isActive(pathname, item.to)}
|
active={isActive(pathname, item.to)}
|
||||||
>
|
>
|
||||||
{item.label}
|
{t(item.labelKey)}
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
{data.viewer == null
|
{data.viewer == null
|
||||||
? (
|
? (
|
||||||
<Button variant="solid" color="neutral" highContrast iconStart={<LockSimpleIcon />}>
|
<Button variant="solid" color="neutral" highContrast iconStart={<LockSimpleIcon />}>
|
||||||
Get Access
|
{t("topBar.getAccess")}
|
||||||
</Button>
|
</Button>
|
||||||
)
|
)
|
||||||
: <TopBarUserMenu identityKey={data.viewer} />}
|
: <TopBarUserMenu identityKey={data.viewer} />}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import { DropdownPopup } from "@probo/ui/src/v2/Dropdown/DropdownPopup";
|
|||||||
import { DropdownSeparator } from "@probo/ui/src/v2/Dropdown/DropdownSeparator";
|
import { DropdownSeparator } from "@probo/ui/src/v2/Dropdown/DropdownSeparator";
|
||||||
import { DropdownTrigger } from "@probo/ui/src/v2/Dropdown/DropdownTrigger";
|
import { DropdownTrigger } from "@probo/ui/src/v2/Dropdown/DropdownTrigger";
|
||||||
import { Text } from "@probo/ui/src/v2/typography/Text";
|
import { Text } from "@probo/ui/src/v2/typography/Text";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { graphql, useFragment } from "react-relay";
|
import { graphql, useFragment } from "react-relay";
|
||||||
|
|
||||||
import type { TopBarUserMenu_identity$key } from "./__generated__/TopBarUserMenu_identity.graphql";
|
import type { TopBarUserMenu_identity$key } from "./__generated__/TopBarUserMenu_identity.graphql";
|
||||||
@@ -38,6 +39,7 @@ interface TopBarUserMenuProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function TopBarUserMenu({ identityKey }: TopBarUserMenuProps) {
|
export function TopBarUserMenu({ identityKey }: TopBarUserMenuProps) {
|
||||||
|
const { t } = useTranslation();
|
||||||
const identity = useFragment(topBarUserMenuFragment, identityKey);
|
const identity = useFragment(topBarUserMenuFragment, identityKey);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -63,7 +65,7 @@ export function TopBarUserMenu({ identityKey }: TopBarUserMenuProps) {
|
|||||||
<DropdownGroupLabel>{identity.email}</DropdownGroupLabel>
|
<DropdownGroupLabel>{identity.email}</DropdownGroupLabel>
|
||||||
<DropdownSeparator />
|
<DropdownSeparator />
|
||||||
<DropdownItem color="error" iconStart={<SignOutIcon />}>
|
<DropdownItem color="error" iconStart={<SignOutIcon />}>
|
||||||
Sign out
|
{t("userMenu.signOut")}
|
||||||
</DropdownItem>
|
</DropdownItem>
|
||||||
</DropdownPopup>
|
</DropdownPopup>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
// PERFORMANCE OF THIS SOFTWARE.
|
// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import type { PreloadedQuery } from "react-relay";
|
import type { PreloadedQuery } from "react-relay";
|
||||||
import { graphql, usePreloadedQuery } from "react-relay";
|
import { graphql, usePreloadedQuery } from "react-relay";
|
||||||
|
|
||||||
@@ -37,11 +38,15 @@ interface HomePageProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function HomePage({ queryRef }: HomePageProps) {
|
export function HomePage({ queryRef }: HomePageProps) {
|
||||||
|
const { t } = useTranslation();
|
||||||
const data = usePreloadedQuery<HomePageQuery>(homePageQuery, queryRef);
|
const data = usePreloadedQuery<HomePageQuery>(homePageQuery, queryRef);
|
||||||
const { organization } = data.currentTrustCenter;
|
const { organization } = data.currentTrustCenter;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Hero title={`Trust at ${organization.name}.`} description={organization.description}>
|
<Hero
|
||||||
|
title={t("home.heroTitle", { name: organization.name })}
|
||||||
|
description={organization.description}
|
||||||
|
>
|
||||||
<OrganizationContactInfo organizationKey={organization} />
|
<OrganizationContactInfo organizationKey={organization} />
|
||||||
</Hero>
|
</Hero>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -14,16 +14,18 @@
|
|||||||
|
|
||||||
import { PlusIcon } from "@phosphor-icons/react";
|
import { PlusIcon } from "@phosphor-icons/react";
|
||||||
import { Button } from "@probo/ui/src/v2/Button/Button";
|
import { Button } from "@probo/ui/src/v2/Button/Button";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { PageHeader } from "#/components/PageHeader/PageHeader";
|
import { PageHeader } from "#/components/PageHeader/PageHeader";
|
||||||
|
|
||||||
export default function RequestsPage() {
|
export default function RequestsPage() {
|
||||||
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
<PageHeader
|
<PageHeader
|
||||||
title="Data Requests"
|
title={t("requests.title")}
|
||||||
actions={(
|
actions={(
|
||||||
<Button variant="soft" color="neutral" highContrast iconStart={<PlusIcon />}>
|
<Button variant="soft" color="neutral" highContrast iconStart={<PlusIcon />}>
|
||||||
New Request
|
{t("requests.newRequest")}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -12,10 +12,13 @@
|
|||||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
// PERFORMANCE OF THIS SOFTWARE.
|
// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { PageHeader } from "#/components/PageHeader/PageHeader";
|
import { PageHeader } from "#/components/PageHeader/PageHeader";
|
||||||
|
|
||||||
// Toolbar (category/region filters, search, Download CSV) and the subprocessor
|
// Toolbar (category/region filters, search, Download CSV) and the subprocessor
|
||||||
// count are deferred until the v2 Select/TextField components exist.
|
// count are deferred until the v2 Select/TextField components exist.
|
||||||
export default function SubprocessorsPage() {
|
export default function SubprocessorsPage() {
|
||||||
return <PageHeader title="Subprocessors" />;
|
const { t } = useTranslation();
|
||||||
|
return <PageHeader title={t("subprocessors.title")} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,16 +14,18 @@
|
|||||||
|
|
||||||
import { BellIcon } from "@phosphor-icons/react";
|
import { BellIcon } from "@phosphor-icons/react";
|
||||||
import { Button } from "@probo/ui/src/v2/Button/Button";
|
import { Button } from "@probo/ui/src/v2/Button/Button";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { PageHeader } from "#/components/PageHeader/PageHeader";
|
import { PageHeader } from "#/components/PageHeader/PageHeader";
|
||||||
|
|
||||||
export default function UpdatesPage() {
|
export default function UpdatesPage() {
|
||||||
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
<PageHeader
|
<PageHeader
|
||||||
title="Updates"
|
title={t("updates.title")}
|
||||||
actions={(
|
actions={(
|
||||||
<Button variant="soft" color="neutral" highContrast iconStart={<BellIcon />}>
|
<Button variant="soft" color="neutral" highContrast iconStart={<BellIcon />}>
|
||||||
Subscribe to updates
|
{t("updates.subscribe")}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user