more i18n

This commit is contained in:
Lokowitz 2025-05-04 16:23:08 +00:00
parent 7eb08474ff
commit 059081ad8b
12 changed files with 122 additions and 62 deletions

View file

@ -10,7 +10,7 @@
"setupCreateSite": "Create Site",
"setupCreateResources": "Create Resources",
"setupOrgName": "Organization Name",
"setupDisplayName": "This is the display name for your organization.",
"orgDisplayName": "This is the display name of your organization.",
"setupOrgId": "Organization ID",
"setupIdentifierMessage": "This is the unique identifier for your organization. This is separate from the display name.",
"setupErrorIdentifier": "Organization ID is already taken. Please choose a different one.",
@ -150,5 +150,46 @@
"proxy": "Proxy",
"rules": "Rules",
"resourceSettingDescription": "Configure the settings on your resource",
"resourceSetting": "{resourceName} Settings"
"resourceSetting": "{resourceName} Settings",
"alwaysAllow": "Always Allow",
"alwaysDeny": "Always Deny",
"orgSettingsDescription": "Configure your organization's general settings",
"orgGeneralSettings": "Organization Settings",
"orgGeneralSettingsDescription": "Manage your organization details and configuration",
"orgGeneralSave": "Save General Settings",
"orgDangerZone": "Danger Zone",
"orgDangerZoneDescription": "Once you delete this org, there is no going back. Please be certain.",
"orgDelete": "Delete Organization",
"orgDeleteConfirm": "Confirm Delete Organization",
"orgMessageRemove": "This action is irreversible and will delete all associated data.",
"orgMessageConfirm": "To confirm, please type the name of the organization below.",
"orgQuestionRemove": "Are you sure you want to remove the organization {selectedOrg}?",
"orgUpdated": "Organization updated",
"orgUpdatedDescription": "The organization has been updated.",
"orgErrorUpdate": "Failed to update organization",
"orgErrorUpdateMessage": "An error occurred while updating the organization.",
"orgErrorFetch": "Failed to fetch organizations",
"orgErrorFetchMessage": "An error occurred while listing your organizations",
"orgErrorDelete": "Failed to delete organization",
"orgErrorDeleteMessage": "An error occurred while deleting the organization.",
"orgDeleted": "Organization deleted",
"orgDeletedMessage": "The organization and its data has been deleted.",
"accessUsersManage": "Manage Users",
"accessUsersDescription": "Invite users and add them to roles to manage access to your organization",
"accessUsersSearch": "Search users...",
"accessUserCreate": "Create User",
"accessUserRemove": "Remove User",
"username": "Username",
"identityProvider": "Identity Provider",
"role": "Role",
"accessRoleNameRequired": "Name is required",
"accessRolesManage": "Manage Roles",
"accessRolesDescription": "Configure roles to manage access to your organization",
"accessRolesSearch": "Search roles...",
"accessRolesAdd": "Add Role",
"accessRoleDelete": "Delete Role",
"description": "Description",
"inviteTitle": "Open Invitations",
"inviteDescription": "Manage your invitations to other users",
"inviteSearch": "Search invitations..."
}

View file

@ -4,6 +4,7 @@ import {
ColumnDef,
} from "@tanstack/react-table";
import { DataTable } from "@app/components/ui/data-table";
import { useTranslations } from 'next-intl';
interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[];
@ -14,12 +15,15 @@ export function InvitationsDataTable<TData, TValue>({
columns,
data
}: DataTableProps<TData, TValue>) {
const t = useTranslations();
return (
<DataTable
columns={columns}
data={data}
title="Invitations"
searchPlaceholder="Search invitations..."
searchPlaceholder={t('inviteSearch')}
searchColumn="email"
/>
);

View file

@ -9,6 +9,7 @@ import UserProvider from "@app/providers/UserProvider";
import { verifySession } from "@app/lib/auth/verifySession";
import AccessPageHeaderAndNav from "../AccessPageHeaderAndNav";
import SettingsSectionTitle from "@app/components/SettingsSectionTitle";
import { getTranslations } from 'next-intl/server';
type InvitationsPageProps = {
params: Promise<{ orgId: string }>;
@ -71,11 +72,13 @@ export default async function InvitationsPage(props: InvitationsPageProps) {
};
});
const t = await getTranslations();
return (
<>
<SettingsSectionTitle
title="Open Invitations"
description="Manage your invitations to other users"
title={t('inviteTitle')}
description={t('inviteDescription')}
/>
<UserProvider user={user!}>
<OrgProvider org={org}>

View file

@ -4,6 +4,7 @@ import {
ColumnDef,
} from "@tanstack/react-table";
import { DataTable } from "@app/components/ui/data-table";
import { useTranslations } from 'next-intl';
interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[];
@ -16,15 +17,18 @@ export function RolesDataTable<TData, TValue>({
data,
createRole
}: DataTableProps<TData, TValue>) {
const t = useTranslations();
return (
<DataTable
columns={columns}
data={data}
title="Roles"
searchPlaceholder="Search roles..."
searchPlaceholder={t('accessRolesSearch')}
searchColumn="name"
onAdd={createRole}
addButtonText="Add Role"
addButtonText={t('accessRolesAdd')}
/>
);
}

View file

@ -19,6 +19,7 @@ import CreateRoleForm from "./CreateRoleForm";
import DeleteRoleForm from "./DeleteRoleForm";
import { createApiClient } from "@app/lib/api";
import { useEnvContext } from "@app/hooks/useEnvContext";
import { useTranslations } from 'next-intl';
export type RoleRow = Role;
@ -38,6 +39,8 @@ export default function UsersTable({ roles: r }: RolesTableProps) {
const { org } = useOrgContext();
const t = useTranslations();
const columns: ColumnDef<RoleRow>[] = [
{
id: "actions",
@ -58,7 +61,7 @@ export default function UsersTable({ roles: r }: RolesTableProps) {
className="h-8 w-8 p-0"
>
<span className="sr-only">
Open menu
{t('openMenu')}
</span>
<MoreHorizontal className="h-4 w-4" />
</Button>
@ -71,7 +74,7 @@ export default function UsersTable({ roles: r }: RolesTableProps) {
}}
>
<span className="text-red-500">
Delete Role
{t('accessRoleDelete')}
</span>
</DropdownMenuItem>
</DropdownMenuContent>
@ -92,7 +95,7 @@ export default function UsersTable({ roles: r }: RolesTableProps) {
column.toggleSorting(column.getIsSorted() === "asc")
}
>
Name
{t('name')}
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
@ -100,7 +103,7 @@ export default function UsersTable({ roles: r }: RolesTableProps) {
},
{
accessorKey: "description",
header: "Description"
header: t('description')
}
];

View file

@ -9,6 +9,7 @@ import RolesTable, { RoleRow } from "./RolesTable";
import { SidebarSettings } from "@app/components/SidebarSettings";
import AccessPageHeaderAndNav from "../AccessPageHeaderAndNav";
import SettingsSectionTitle from "@app/components/SettingsSectionTitle";
import { getTranslations } from 'next-intl/server';
type RolesPageProps = {
params: Promise<{ orgId: string }>;
@ -62,12 +63,13 @@ export default async function RolesPage(props: RolesPageProps) {
}
const roleRows: RoleRow[] = roles;
const t = await getTranslations();
return (
<>
<SettingsSectionTitle
title="Manage Roles"
description="Configure roles to manage access to your organization"
title={t('accessRolesManage')}
description={t('accessRolesDescription')}
/>
<OrgProvider org={org}>
<RolesTable roles={roleRows} />

View file

@ -4,6 +4,7 @@ import {
ColumnDef,
} from "@tanstack/react-table";
import { DataTable } from "@app/components/ui/data-table";
import { useTranslations } from 'next-intl';
interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[];
@ -16,15 +17,18 @@ export function UsersDataTable<TData, TValue>({
data,
inviteUser
}: DataTableProps<TData, TValue>) {
const t = useTranslations();
return (
<DataTable
columns={columns}
data={data}
title="Users"
searchPlaceholder="Search users..."
searchPlaceholder={t('accessUsersSearch')}
searchColumn="email"
onAdd={inviteUser}
addButtonText="Create User"
addButtonText={t('accessUserCreate')}
/>
);
}

View file

@ -20,6 +20,7 @@ import { formatAxiosError } from "@app/lib/api";
import { createApiClient } from "@app/lib/api";
import { useEnvContext } from "@app/hooks/useEnvContext";
import { useUserContext } from "@app/hooks/useUserContext";
import { useTranslations } from 'next-intl';
export type UserRow = {
id: string;
@ -47,6 +48,7 @@ export default function UsersTable({ users: u }: UsersTableProps) {
const api = createApiClient(useEnvContext());
const { user, updateUser } = useUserContext();
const { org } = useOrgContext();
const t = useTranslations();
const columns: ColumnDef<UserRow>[] = [
{
@ -68,7 +70,7 @@ export default function UsersTable({ users: u }: UsersTableProps) {
className="h-8 w-8 p-0"
>
<span className="sr-only">
Open menu
{t('openMenu')}
</span>
<MoreHorizontal className="h-4 w-4" />
</Button>
@ -79,7 +81,7 @@ export default function UsersTable({ users: u }: UsersTableProps) {
className="block w-full"
>
<DropdownMenuItem>
Manage User
{t('accessUsersManage')}
</DropdownMenuItem>
</Link>
{`${userRow.username}-${userRow.idpId}` !==
@ -95,7 +97,7 @@ export default function UsersTable({ users: u }: UsersTableProps) {
}}
>
<span className="text-red-500">
Remove User
{t('accessUserRemove')}
</span>
</DropdownMenuItem>
)}
@ -118,7 +120,7 @@ export default function UsersTable({ users: u }: UsersTableProps) {
column.toggleSorting(column.getIsSorted() === "asc")
}
>
Username
{t('username')}
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
@ -134,7 +136,7 @@ export default function UsersTable({ users: u }: UsersTableProps) {
column.toggleSorting(column.getIsSorted() === "asc")
}
>
Identity Provider
{t('identityProvider')}
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
@ -150,7 +152,7 @@ export default function UsersTable({ users: u }: UsersTableProps) {
column.toggleSorting(column.getIsSorted() === "asc")
}
>
Role
{t('role')}
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);

View file

@ -10,6 +10,7 @@ import UserProvider from "@app/providers/UserProvider";
import { verifySession } from "@app/lib/auth/verifySession";
import AccessPageHeaderAndNav from "../AccessPageHeaderAndNav";
import SettingsSectionTitle from "@app/components/SettingsSectionTitle";
import { getTranslations } from 'next-intl/server';
type UsersPageProps = {
params: Promise<{ orgId: string }>;
@ -83,11 +84,13 @@ export default async function UsersPage(props: UsersPageProps) {
};
});
const t = await getTranslations();
return (
<>
<SettingsSectionTitle
title="Manage Users"
description="Invite users and add them to roles to manage access to your organization"
title={t('accessUsersManage')}
description={t('accessUsersDescription')}
/>
<UserProvider user={user!}>
<OrgProvider org={org}>

View file

@ -10,6 +10,7 @@ import { GetOrgUserResponse } from "@server/routers/user";
import { AxiosResponse } from "axios";
import { redirect } from "next/navigation";
import { cache } from "react";
import { getTranslations } from 'next-intl/server';
type GeneralSettingsProps = {
children: React.ReactNode;
@ -57,9 +58,11 @@ export default async function GeneralSettingsPage({
redirect(`/${orgId}`);
}
const t = await getTranslations();
const navItems = [
{
title: "General",
title: t('general'),
href: `/{orgId}/settings/general`,
},
];
@ -69,8 +72,8 @@ export default async function GeneralSettingsPage({
<OrgProvider org={org}>
<OrgUserProvider orgUser={orgUser}>
<SettingsSectionTitle
title="General"
description="Configure your organization's general settings"
title={t('general')}
description={t('orgSettingsDescription')}
/>
<HorizontalTabs items={navItems}>

View file

@ -44,6 +44,7 @@ import {
SettingsSectionFooter
} from "@app/components/Settings";
import { useUserContext } from "@app/hooks/useUserContext";
import { useTranslations } from 'next-intl';
const GeneralFormSchema = z.object({
name: z.string()
@ -79,8 +80,8 @@ export default function GeneralPage() {
);
toast({
title: "Organization deleted",
description: "The organization and its data has been deleted."
title: t('orgDeleted'),
description: t('orgDeletedMessage')
});
if (res.status === 200) {
@ -90,11 +91,8 @@ export default function GeneralPage() {
console.error(err);
toast({
variant: "destructive",
title: "Failed to delete org",
description: formatAxiosError(
err,
"An error occurred while deleting the org."
)
title: t('orgErrorDelete'),
description: formatAxiosError(err,t('orgErrorDeleteMessage'))
});
} finally {
setLoadingDelete(false);
@ -121,11 +119,8 @@ export default function GeneralPage() {
console.error(err);
toast({
variant: "destructive",
title: "Failed to fetch orgs",
description: formatAxiosError(
err,
"An error occurred while listing your orgs"
)
title: t('orgErrorFetch'),
description: formatAxiosError(err,t('orgErrorFetchMessage'))
});
}
}
@ -138,8 +133,8 @@ export default function GeneralPage() {
})
.then(() => {
toast({
title: "Organization updated",
description: "The organization has been updated."
title: t('orgUpdated'),
description: t('orgUpdatedDescription')
});
router.refresh();
@ -147,11 +142,8 @@ export default function GeneralPage() {
.catch((e) => {
toast({
variant: "destructive",
title: "Failed to update org",
description: formatAxiosError(
e,
"An error occurred while updating the org."
)
title: t('orgErrorUpdate'),
description: formatAxiosError(e,t('orgErrorUpdateMessage'))
});
})
.finally(() => {
@ -159,6 +151,8 @@ export default function GeneralPage() {
});
}
const t = useTranslations();
return (
<SettingsContainer>
<ConfirmDeleteDialog
@ -169,31 +163,29 @@ export default function GeneralPage() {
dialog={
<div>
<p className="mb-2">
Are you sure you want to delete the organization{" "}
<b>{org?.org.name}?</b>
{t('orgQuestionRemove', {selectedOrg: org?.org.name})}
</p>
<p className="mb-2">
This action is irreversible and will delete all
associated data.
{t('orgMessageRemove')}
</p>
<p>
To confirm, type the name of the organization below.
{t('orgMessageConfirm')}
</p>
</div>
}
buttonText="Confirm Delete Organization"
buttonText={t('orgDeleteConfirm')}
onConfirm={deleteOrg}
string={org?.org.name || ""}
title="Delete Organization"
title={t('orgDelete')}
/>
<SettingsSection>
<SettingsSectionHeader>
<SettingsSectionTitle>
Organization Settings
{t('orgGeneralSettings')}
</SettingsSectionTitle>
<SettingsSectionDescription>
Manage your organization details and configuration
{t('orgGeneralSettingsDescription')}
</SettingsSectionDescription>
</SettingsSectionHeader>
@ -210,14 +202,13 @@ export default function GeneralPage() {
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Name</FormLabel>
<FormLabel>{t('name')}</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
<FormDescription>
This is the display name of the
organization.
{t('orgDisplayName')}
</FormDescription>
</FormItem>
)}
@ -234,17 +225,16 @@ export default function GeneralPage() {
loading={loadingSave}
disabled={loadingSave}
>
Save General Settings
{t('orgGeneralSave')}
</Button>
</SettingsSectionFooter>
</SettingsSection>
<SettingsSection>
<SettingsSectionHeader>
<SettingsSectionTitle>Danger Zone</SettingsSectionTitle>
<SettingsSectionTitle>{t('orgDangerZone')}</SettingsSectionTitle>
<SettingsSectionDescription>
Once you delete this org, there is no going back. Please
be certain.
{t('orgDangerZoneDescription')}
</SettingsSectionDescription>
</SettingsSectionHeader>
@ -256,7 +246,7 @@ export default function GeneralPage() {
loading={loadingDelete}
disabled={loadingDelete}
>
Delete Organization Data
{t('orgDelete')}
</Button>
</SettingsSectionFooter>
</SettingsSection>

View file

@ -1,4 +1,5 @@
"use client";
import { useEffect, useState, use } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";