Apply i18n to client components

This commit is contained in:
Takeshi Kimata
2024-05-03 17:39:07 +09:00
parent 72c71efedc
commit e25acad76c
8 changed files with 146 additions and 83 deletions

View File

@@ -3,21 +3,21 @@
"title": "Hello world!" "title": "Hello world!"
}, },
"Projects": { "Projects": {
"projects": "プロジェクト", "projects": "Projects",
"project": "プロジェクト", "project": "Project",
"new_project": "新規プロジェクト", "new_project": "New Project",
"edit_project": "プロジェクトの編集", "edit_project": "Edit Project",
"delete_project": "プロジェクトの削除", "delete_project": "Delete Project",
"id": "ID", "id": "ID",
"name": "名前", "name": "Name",
"detail": "詳細", "detail": "Detail",
"last_update": "最終更新", "last_update": "Last update",
"actions": "アクション", "actions": "Actions",
"project_name": "プロジェクト名", "project_name": "Project Name",
"project_detail": "プロジェクト詳細", "project_detail": "Project Detail",
"close": "閉じる", "close": "Close",
"create": "作成", "create": "Create",
"update": "作成", "update": "Update",
"please_enter": "プロジェクト名を入力してください" "please_enter": "Please enter project name"
} }
} }

View File

@@ -3,21 +3,21 @@
"title": "はろー" "title": "はろー"
}, },
"Projects": { "Projects": {
"projects": "Projects", "projects": "プロジェクト",
"project": "Project", "project": "プロジェクト",
"new_project": "New Project", "new_project": "新規プロジェクト",
"edit_project": "Edit Project", "edit_project": "プロジェクトの編集",
"delete_project": "Delete Project", "delete_project": "プロジェクトの削除",
"id": "ID", "id": "ID",
"name": "Name", "name": "名前",
"detail": "Detail", "detail": "詳細",
"last_update": "Last update", "last_update": "最終更新",
"actions": "Actions", "actions": "アクション",
"project_name": "Project Name", "project_name": "プロジェクト名",
"project_detail": "Project Detail", "project_detail": "プロジェクト詳細",
"close": "Close", "close": "閉じる",
"create": "Create", "create": "作成",
"update": "Update", "update": "更新",
"please_enter": "Please enter project name" "please_enter": "プロジェクト名を入力してください"
} }
} }

View File

@@ -1,7 +1,6 @@
import { import {
Navbar as NextUINavbar, Navbar as NextUINavbar,
NavbarContent, NavbarContent,
NavbarMenuToggle,
NavbarBrand, NavbarBrand,
NavbarItem, NavbarItem,
Link as NextUiLink Link as NextUiLink
@@ -36,7 +35,20 @@ export default function Header(params: { locale: string }) {
<p className="font-bold text-inherit">Test Case Manager</p> <p className="font-bold text-inherit">Test Case Manager</p>
</Link> </Link>
</NavbarBrand> </NavbarBrand>
{siteConfig.navItems.map((item) => ( <ul className="hidden lg:flex gap-4 justify-start ml-2">
{siteConfig.navItems.map((item) => (
<NavbarItem key={item.href}>
<Link
className="data-[active=true]:text-primary data-[active=true]:font-medium"
href={item.href}
locale={params.locale}
>
{item.label}
</Link>
</NavbarItem>
))}
</ul>
{/* {siteConfig.navItems.map((item) => (
<NavbarItem key={item.href}> <NavbarItem key={item.href}>
<Link <Link
className="data-[active=true]:text-primary data-[active=true]:font-medium" className="data-[active=true]:text-primary data-[active=true]:font-medium"
@@ -46,7 +58,7 @@ export default function Header(params: { locale: string }) {
{item.label} {item.label}
</Link> </Link>
</NavbarItem> </NavbarItem>
))} ))} */}
</NavbarContent> </NavbarContent>
<NavbarContent className="basis-1 pl-4" justify="end"> <NavbarContent className="basis-1 pl-4" justify="end">

View File

@@ -11,13 +11,14 @@ import {
ModalBody, ModalBody,
ModalFooter, ModalFooter,
} from "@nextui-org/react"; } from "@nextui-org/react";
import { ProjectType } from "@/types/project"; import { ProjectType, ProjectsMessages } from "@/types/project";
type Props = { type Props = {
isOpen: boolean; isOpen: boolean;
editingProject: ProjectType; editingProject: ProjectType;
onCancel: () => void; onCancel: () => void;
onSubmit: (name: string, detail: string) => void; onSubmit: (name: string, detail: string) => void;
messages: ProjectsMessages;
}; };
export default function ProjectDialog({ export default function ProjectDialog({
@@ -25,6 +26,7 @@ export default function ProjectDialog({
editingProject, editingProject,
onCancel, onCancel,
onSubmit, onSubmit,
messages,
}: Props) { }: Props) {
const [projectName, setProjectName] = useState({ const [projectName, setProjectName] = useState({
text: editingProject ? editingProject.name : "", text: editingProject ? editingProject.name : "",
@@ -80,7 +82,7 @@ export default function ProjectDialog({
setProjectName({ setProjectName({
text: "", text: "",
isValid: false, isValid: false,
errorMessage: "Please enter project name", errorMessage: messages.pleaseEnter,
}); });
return; return;
@@ -98,11 +100,13 @@ export default function ProjectDialog({
}} }}
> >
<ModalContent> <ModalContent>
<ModalHeader className="flex flex-col gap-1">Project</ModalHeader> <ModalHeader className="flex flex-col gap-1">
{messages.project}
</ModalHeader>
<ModalBody> <ModalBody>
<Input <Input
type="text" type="text"
label="Project Name" label={messages.projectName}
value={projectName.text} value={projectName.text}
isInvalid={projectName.isValid} isInvalid={projectName.isValid}
errorMessage={projectName.errorMessage} errorMessage={projectName.errorMessage}
@@ -114,7 +118,7 @@ export default function ProjectDialog({
}} }}
/> />
<Textarea <Textarea
label="Project Detail" label={messages.projectDetail}
value={projectDetail.text} value={projectDetail.text}
isInvalid={projectDetail.isValid} isInvalid={projectDetail.isValid}
errorMessage={projectDetail.errorMessage} errorMessage={projectDetail.errorMessage}
@@ -128,10 +132,10 @@ export default function ProjectDialog({
</ModalBody> </ModalBody>
<ModalFooter> <ModalFooter>
<Button variant="light" onPress={onCancel}> <Button variant="light" onPress={onCancel}>
Close {messages.close}
</Button> </Button>
<Button color="primary" onPress={validate}> <Button color="primary" onPress={validate}>
{editingProject ? "Update" : "Create"} {editingProject ? messages.update : messages.create}
</Button> </Button>
</ModalFooter> </ModalFooter>
</ModalContent> </ModalContent>

View File

@@ -1,8 +1,8 @@
// "use client"; "use client";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Button } from "@nextui-org/react"; import { Button } from "@nextui-org/react";
import { Plus } from "lucide-react"; import { Plus } from "lucide-react";
import { ProjectType } from "@/types/project"; import { ProjectType, ProjectsMessages } from "@/types/project";
import ProjectsTable from "./ProjectsTable"; import ProjectsTable from "./ProjectsTable";
import ProjectDialog from "./ProjectDialog"; import ProjectDialog from "./ProjectDialog";
import { import {
@@ -11,10 +11,12 @@ import {
updateProject, updateProject,
deleteProject, deleteProject,
} from "./projectsControl"; } from "./projectsControl";
import { useTranslations } from "next-intl";
export default function ProjectsPage() { export type Props = {
const t = useTranslations("ProjectsPage"); messages: ProjectsMessages;
};
export default function ProjectsPage({ messages }: Props) {
const [projects, setProjects] = useState([]); const [projects, setProjects] = useState([]);
useEffect(() => { useEffect(() => {
@@ -78,33 +80,37 @@ export default function ProjectsPage() {
}; };
return ( return (
<div className="container mx-auto max-w-3xl pt-16 px-6 flex-grow"> <div className="flex justify-center">
<div className="w-full p-3 flex items-center justify-between"> <div className="container mx-auto max-w-3xl pt-16 px-6 flex-grow">
<h3 className="font-bold">{t("project.project")}</h3> <div className="w-full p-3 flex items-center justify-between">
<div> <h3 className="font-bold">{messages.project}</h3>
<Button <div>
startContent={<Plus size={16} />} <Button
size="sm" startContent={<Plus size={16} />}
color="primary" size="sm"
onClick={openDialogForCreate} color="primary"
> onClick={openDialogForCreate}
{t("newProject")} >
</Button> {messages.newProject}
</Button>
</div>
</div> </div>
<ProjectsTable
projects={projects}
onEditProject={onEditClick}
onDeleteProject={onDeleteClick}
messages={messages}
/>
<ProjectDialog
isOpen={isProjectDialogOpen}
editingProject={editingProject}
onCancel={closeDialog}
onSubmit={onSubmit}
messages={messages}
/>
</div> </div>
<ProjectsTable
projects={projects}
onEditProject={onEditClick}
onDeleteProject={onDeleteClick}
/>
<ProjectDialog
isOpen={isProjectDialogOpen}
editingProject={editingProject}
onCancel={closeDialog}
onSubmit={onSubmit}
/>
</div> </div>
); );
} }

View File

@@ -15,28 +15,30 @@ import {
Link, Link,
} from "@nextui-org/react"; } from "@nextui-org/react";
import { MoreVertical } from "lucide-react"; import { MoreVertical } from "lucide-react";
import { ProjectType } from "@/types/project"; import { ProjectType, ProjectsMessages } from "@/types/project";
import dayjs from "dayjs"; import dayjs from "dayjs";
const headerColumns = [
{ name: "ID", uid: "id", sortable: true },
{ name: "Name", uid: "name", sortable: true },
{ name: "Detail", uid: "detail", sortable: true },
{ name: "Last update", uid: "updatedAt", sortable: true },
{ name: "Actions", uid: "actions" },
];
type Props = { type Props = {
projects: ProjectType[]; projects: ProjectType[];
onEditProject: (project: ProjectType) => void; onEditProject: (project: ProjectType) => void;
onDeleteProject: (projectId: number) => void; onDeleteProject: (projectId: number) => void;
messages: ProjectsMessages;
}; };
export default function ProjectsTable({ export default function ProjectsTable({
projects, projects,
onEditProject, onEditProject,
onDeleteProject, onDeleteProject,
messages,
}: Props) { }: Props) {
const headerColumns = [
{ name: messages.id, uid: "id", sortable: true },
{ name: messages.name, uid: "name", sortable: true },
{ name: messages.detail, uid: "detail", sortable: true },
{ name: messages.lastUpdate, uid: "updatedAt", sortable: true },
{ name: messages.actions, uid: "actions" },
];
const [sortDescriptor, setSortDescriptor] = useState<SortDescriptor>({ const [sortDescriptor, setSortDescriptor] = useState<SortDescriptor>({
column: "id", column: "id",
direction: "ascending", direction: "ascending",
@@ -92,13 +94,13 @@ export default function ProjectsTable({
</DropdownTrigger> </DropdownTrigger>
<DropdownMenu aria-label="project actions"> <DropdownMenu aria-label="project actions">
<DropdownItem onClick={() => onEditProject(project)}> <DropdownItem onClick={() => onEditProject(project)}>
Edit project {messages.editProject}
</DropdownItem> </DropdownItem>
<DropdownItem <DropdownItem
className="text-danger" className="text-danger"
onClick={() => onDeleteProject(project.id)} onClick={() => onDeleteProject(project.id)}
> >
Delete project {messages.deleteProject}
</DropdownItem> </DropdownItem>
</DropdownMenu> </DropdownMenu>
</Dropdown> </Dropdown>

View File

@@ -1,9 +1,29 @@
import ProjectsPage from "./ProjectsPage"; import ProjectsPage from "./ProjectsPage";
import { useTranslations } from "next-intl";
export default function Page() { export default function Page() {
const t = useTranslations("Projects");
const messages = {
projects: t("projects"),
project: t("project"),
newProject: t("new_project"),
editProject: t("edit_project"),
deleteProject: t("delete_project"),
id: t("id"),
name: t("name"),
detail: t("detail"),
lastUpdate: t("last_update"),
actions: t("actions"),
projectName: t("project_name"),
projectDetail: t("project_detail"),
close: t("close"),
create: t("create"),
update: t("update"),
pleaseEnter: t("please_enter"),
};
return ( return (
<> <>
<ProjectsPage /> <ProjectsPage messages={messages} />
</> </>
); );
} }

View File

@@ -5,3 +5,22 @@ export type ProjectType = {
createdAt: string; createdAt: string;
updatedAt: string; updatedAt: string;
}; };
export type ProjectsMessages = {
project: string;
projects: string;
newProject: string;
editProject: string;
deleteProject: string;
id: string;
name: string;
detail: string;
lastUpdate: string;
actions: string;
projectName: string;
projectDetail: string;
close: string;
create: string;
update: string;
pleaseEnter: string;
};