Apply i18n

This commit is contained in:
Takeshi Kimata
2024-05-05 17:51:28 +09:00
parent 520f6a98dd
commit b04ff770f8
8 changed files with 219 additions and 54 deletions

View File

@@ -6,19 +6,19 @@ const priorities = [
]; ];
const testTypes = [ const testTypes = [
{ name: "Other", uid: "other" }, { uid: "other" },
{ name: "Security", uid: "security" }, { uid: "security" },
{ name: "Performance", uid: "performance" }, { uid: "performance" },
{ name: "Accessibility", uid: "accessibility" }, { uid: "accessibility" },
{ name: "Functional", uid: "functional" }, { uid: "functional" },
{ name: "Acceptance", uid: "acceptance" }, { uid: "acceptance" },
{ name: "Usability", uid: "usability" }, { uid: "usability" },
{ name: "Smoke&Sanity", uid: "smoke-sanity" }, { uid: "smokeSanity" },
{ name: "Compatibility", uid: "compatibility" }, { uid: "compatibility" },
{ name: "Destructive", uid: "destructive" }, { uid: "destructive" },
{ name: "Regression", uid: "regression" }, { uid: "regression" },
{ name: "Automated", uid: "automated" }, { uid: "automated" },
{ name: "Manual", uid: "manual" }, { uid: "manual" },
]; ];
const automationStatus = [ const automationStatus = [
@@ -28,10 +28,7 @@ const automationStatus = [
{ name: "Obsolete", uid: "obsolete" }, { name: "Obsolete", uid: "obsolete" },
]; ];
const templates = [ const templates = [{ uid: "text" }, { uid: "step" }];
{ name: "Text", uid: "text-template" },
{ name: "Step", uid: "step-template" },
];
const testRunStatus = [ const testRunStatus = [
{ name: "New", uid: "new" }, { name: "New", uid: "new" },

View File

@@ -53,10 +53,51 @@
"low": "Low" "low": "Low"
}, },
"Case": { "Case": {
"back_to_cases": "Back to test cases",
"updating": "Updating...",
"update": "Update",
"basic": "Basic",
"title": "Title",
"please_enter_title": "Please enter title",
"description": "Description",
"test_case_description": "Test case description",
"priority": "Priority",
"critical": "Critical", "critical": "Critical",
"high": "High", "high": "High",
"medium": "Medium", "medium": "Medium",
"low": "Low" "low": "Low",
"type": "Type",
"other": "Other",
"security": "Security",
"performance": "Performance",
"accessibility": "Accessibility",
"functional": "Functional",
"acceptance": "Acceptance",
"usability": "Usability",
"smoke_sanity": "Smoke&Sanity",
"compatibility": "Compatibility",
"destructive": "Destructive",
"regression": "Regression",
"automated": "Automated",
"manual": "Manual",
"template": "Template",
"test_detail": "Test detail",
"preconditions": "Preconditions",
"step": "Step",
"text": "Text",
"steps": "Steps",
"new_step": "New Step",
"details_of_the_step": "Details of the step",
"expected_result": "Expected result",
"delete_this_step": "Delete this step",
"insert_step": "Insert step",
"attachments": "Attachments",
"delete": "Delete",
"download": "Download",
"delete_file": "Delete file",
"click_to_upload": "Click to upload",
"or_drag_and_drop": "or drag and drop",
"max_file_size": "Max. file size"
}, },
"Runs": { "Runs": {
"id": "ID", "id": "ID",

View File

@@ -53,10 +53,51 @@
"low": "低" "low": "低"
}, },
"Case": { "Case": {
"back_to_cases": "テストケース一覧に戻る",
"updating": "更新中...",
"update": "更新",
"basic": "基本データ",
"title": "タイトル",
"please_enter_title": "タイトルを入力してください",
"description": "詳細",
"test_case_description": "テストケース詳細",
"priority": "優先度",
"critical": "致", "critical": "致",
"high": "高", "high": "高",
"medium": "中", "medium": "中",
"low": "低" "low": "低",
"type": "タイプ",
"other": "その他",
"security": "セキュリティ",
"performance": "パフォーマンス",
"accessibility": "アクセシビリティ",
"functional": "機能",
"acceptance": "受け入れ",
"usability": "ユーザビリティ",
"smoke_sanity": "スモーク/サニティ",
"compatibility": "互換性",
"destructive": "破壊",
"regression": "回帰",
"automated": "自動",
"manual": "手動",
"template": "テンプレート",
"test_detail": "テスト詳細",
"preconditions": "前提条件",
"step": "ステップ",
"text": "テキスト",
"steps": "ステップ",
"new_step": "新規ステップ",
"details_of_the_step": "ステップ詳細",
"expected_result": "期待結果",
"delete_this_step": "このステップを削除",
"insert_step": "ステップを挿入",
"attachments": "添付ファイル",
"delete": "削除",
"download": "ダウンロード",
"delete_file": "ファイルを削除",
"click_to_upload": "クリックしてアップロード",
"or_drag_and_drop": "またはドラッグアンドドロップ",
"max_file_size": "最大ファイルサイズ"
}, },
"Runs": { "Runs": {
"id": "ID", "id": "ID",

View File

@@ -1,5 +1,5 @@
import { Image, Button, Tooltip, Card, CardBody } from "@nextui-org/react"; import { Image, Button, Tooltip, Card, CardBody } from "@nextui-org/react";
import { AttachmentType } from "../../../../../../../../types/case"; import { AttachmentType, CaseMessages } from "@/types/case";
import { Trash, ArrowDownToLine } from "lucide-react"; import { Trash, ArrowDownToLine } from "lucide-react";
type Props = { type Props = {
@@ -9,12 +9,14 @@ type Props = {
downloadFileName: string downloadFileName: string
) => void; ) => void;
onAttachmentDelete: (attachmentId: number) => void; onAttachmentDelete: (attachmentId: number) => void;
messages: CaseMessages,
}; };
export default function CaseAttachmentsEditor({ export default function CaseAttachmentsEditor({
attachments = [], attachments = [],
onAttachmentDownload, onAttachmentDownload,
onAttachmentDelete, onAttachmentDelete,
messages,
}: Props) { }: Props) {
let images = []; let images = [];
let others = []; let others = [];
@@ -48,7 +50,7 @@ export default function CaseAttachmentsEditor({
/> />
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<p>{image.title}</p> <p>{image.title}</p>
<Tooltip content="Delete"> <Tooltip content={messages.delete}>
<Button <Button
isIconOnly isIconOnly
size="sm" size="sm"
@@ -70,7 +72,7 @@ export default function CaseAttachmentsEditor({
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<p>{file.title}</p> <p>{file.title}</p>
<div> <div>
<Tooltip content="Download"> <Tooltip content={messages.download}>
<Button <Button
isIconOnly isIconOnly
size="sm" size="sm"
@@ -80,7 +82,7 @@ export default function CaseAttachmentsEditor({
<ArrowDownToLine size={16} /> <ArrowDownToLine size={16} />
</Button> </Button>
</Tooltip> </Tooltip>
<Tooltip content="Delete"> <Tooltip content={messages.delete}>
<Button <Button
isIconOnly isIconOnly
size="sm" size="sm"

View File

@@ -170,7 +170,7 @@ export default function CaseEditor({
<> <>
<div className="border-b-1 dark:border-neutral-700 w-full p-3 flex items-center justify-between"> <div className="border-b-1 dark:border-neutral-700 w-full p-3 flex items-center justify-between">
<div className="flex items-center"> <div className="flex items-center">
<Tooltip content="Back to cases" placement="left"> <Tooltip content={params.messages.backToCases} placement="left">
<Button <Button
isIconOnly isIconOnly
size="sm" size="sm"
@@ -198,20 +198,20 @@ export default function CaseEditor({
setIsUpdating(false); setIsUpdating(false);
}} }}
> >
{isUpdating ? "Updating..." : "Update"} {isUpdating ? params.messages.updating : params.messages.update}
</Button> </Button>
</div> </div>
<div className="p-5"> <div className="p-5">
<h6 className="font-bold">Basic</h6> <h6 className="font-bold">{params.messages.basic}</h6>
<Input <Input
size="sm" size="sm"
type="text" type="text"
variant="bordered" variant="bordered"
label="Title" label={params.messages.title}
value={testCase.title} value={testCase.title}
isInvalid={isTitleInvalid} isInvalid={isTitleInvalid}
errorMessage={isTitleInvalid ? "please enter title" : ""} errorMessage={isTitleInvalid ? params.messages.pleaseEnterTitle : ""}
onChange={(e) => { onChange={(e) => {
setTestCase({ ...testCase, title: e.target.value }); setTestCase({ ...testCase, title: e.target.value });
}} }}
@@ -221,8 +221,8 @@ export default function CaseEditor({
<Textarea <Textarea
size="sm" size="sm"
variant="bordered" variant="bordered"
label="Description" label={params.messages.description}
placeholder="Test case description" placeholder={params.messages.testCaseDescription}
value={testCase.description} value={testCase.description}
onValueChange={(changeValue) => { onValueChange={(changeValue) => {
setTestCase({ ...testCase, description: changeValue }); setTestCase({ ...testCase, description: changeValue });
@@ -249,7 +249,7 @@ export default function CaseEditor({
fill={priorities[testCase.priority].color} fill={priorities[testCase.priority].color}
/> />
} }
label="Priority" label={params.messages.priority}
className="mt-3 max-w-xs" className="mt-3 max-w-xs"
> >
{priorities.map((priority, index) => ( {priorities.map((priority, index) => (
@@ -272,12 +272,12 @@ export default function CaseEditor({
); );
setTestCase({ ...testCase, type: index }); setTestCase({ ...testCase, type: index });
}} }}
label="type" label={params.messages.type}
className="mt-3 max-w-xs" className="mt-3 max-w-xs"
> >
{testTypes.map((type, index) => ( {testTypes.map((type, index) => (
<SelectItem key={type.uid} value={index}> <SelectItem key={type.uid} value={index}>
{type.name} {params.messages[type.uid]}
</SelectItem> </SelectItem>
))} ))}
</Select> </Select>
@@ -295,27 +295,26 @@ export default function CaseEditor({
); );
setTestCase({ ...testCase, template: index }); setTestCase({ ...testCase, template: index });
}} }}
label="template" label={params.messages.template}
className="mt-3 max-w-xs" className="mt-3 max-w-xs"
> >
{templates.map((template, index) => ( {templates.map((template, index) => (
<SelectItem key={template.uid} value={index}> <SelectItem key={template.uid} value={index}>
{template.name} {params.messages[template.uid]}
</SelectItem> </SelectItem>
))} ))}
</Select> </Select>
</div> </div>
<Divider className="my-6" /> <Divider className="my-6" />
{templates[testCase.template].name === "Text" ? ( {templates[testCase.template].uid === "text" ? (
<div> <div>
<h6 className="font-bold">Test Detail</h6> <h6 className="font-bold">{params.messages.testDetail}</h6>
<div className="flex"> <div className="flex">
<Textarea <Textarea
size="sm" size="sm"
variant="bordered" variant="bordered"
label="PreConditions" label={params.messages.preconditions}
placeholder="PreConditions"
value={testCase.preConditions} value={testCase.preConditions}
onValueChange={(changeValue) => { onValueChange={(changeValue) => {
setTestCase({ ...testCase, preConditions: changeValue }); setTestCase({ ...testCase, preConditions: changeValue });
@@ -326,8 +325,7 @@ export default function CaseEditor({
<Textarea <Textarea
size="sm" size="sm"
variant="bordered" variant="bordered"
label="ExpectedResults" label={params.messages.expectedResult}
placeholder="ExpectedResults"
value={testCase.expectedResults} value={testCase.expectedResults}
onValueChange={(changeValue) => { onValueChange={(changeValue) => {
setTestCase({ ...testCase, expectedResults: changeValue }); setTestCase({ ...testCase, expectedResults: changeValue });
@@ -339,7 +337,7 @@ export default function CaseEditor({
) : ( ) : (
<div> <div>
<div className="flex items-center"> <div className="flex items-center">
<h6 className="font-bold">Steps</h6> <h6 className="font-bold">{params.messages.steps}</h6>
<Button <Button
startContent={<Plus size={16} />} startContent={<Plus size={16} />}
size="sm" size="sm"
@@ -347,7 +345,7 @@ export default function CaseEditor({
className="ms-3" className="ms-3"
onPress={() => onPlusClick(1)} onPress={() => onPlusClick(1)}
> >
New Step {params.messages.newStep}
</Button> </Button>
</div> </div>
<CaseStepsEditor <CaseStepsEditor
@@ -366,12 +364,13 @@ export default function CaseEditor({
}} }}
onStepPlus={onPlusClick} onStepPlus={onPlusClick}
onStepDelete={onDeleteClick} onStepDelete={onDeleteClick}
messages={params.messages}
/> />
</div> </div>
)} )}
<Divider className="my-6" /> <Divider className="my-6" />
<h6 className="font-bold">Attachments</h6> <h6 className="font-bold">{params.messages.attachments}</h6>
<CaseAttachmentsEditor <CaseAttachmentsEditor
attachments={testCase.Attachments} attachments={testCase.Attachments}
onAttachmentDownload={( onAttachmentDownload={(
@@ -379,6 +378,7 @@ export default function CaseEditor({
downloadFileName: string downloadFileName: string
) => fetchDownloadAttachment(attachmentId, downloadFileName)} ) => fetchDownloadAttachment(attachmentId, downloadFileName)}
onAttachmentDelete={onAttachmentDelete} onAttachmentDelete={onAttachmentDelete}
messages={params.messages}
/> />
<div <div
className="flex items-center justify-center w-96 mt-3" className="flex items-center justify-center w-96 mt-3"
@@ -392,11 +392,13 @@ export default function CaseEditor({
<div className="flex flex-col items-center justify-center pt-5 pb-6"> <div className="flex flex-col items-center justify-center pt-5 pb-6">
<ArrowUpFromLine /> <ArrowUpFromLine />
<p className="mb-2 text-sm text-neutral-500 dark:text-neutral-400"> <p className="mb-2 text-sm text-neutral-500 dark:text-neutral-400">
<span className="font-semibold">Click to upload</span> or drag <span className="font-semibold">
and drop {params.messages.clickToUpload}
</span>
<span>{params.messages.orDragAndDrop}</span>
</p> </p>
<p className="text-xs text-neutral-500 dark:text-neutral-400"> <p className="text-xs text-neutral-500 dark:text-neutral-400">
Max. file size: 50 MB {params.messages.maxFileSize}: 50 MB
</p> </p>
</div> </div>
<input <input

View File

@@ -1,5 +1,5 @@
import { Textarea, Button, Tooltip } from "@nextui-org/react"; import { Textarea, Button, Tooltip } from "@nextui-org/react";
import { StepType } from "../../../../../../../../types/case"; import { CaseMessages, StepType } from "@/types/case";
import { Plus, Trash } from "lucide-react"; import { Plus, Trash } from "lucide-react";
type Props = { type Props = {
@@ -7,6 +7,7 @@ type Props = {
onStepUpdate: (stepId: number, step: StepType) => void; onStepUpdate: (stepId: number, step: StepType) => void;
onStepPlus: (newStepNo: number) => void; onStepPlus: (newStepNo: number) => void;
onStepDelete: (stepId: number) => void; onStepDelete: (stepId: number) => void;
messages: CaseMessages;
}; };
export default function StepsEditor({ export default function StepsEditor({
@@ -14,6 +15,7 @@ export default function StepsEditor({
onStepUpdate, onStepUpdate,
onStepPlus, onStepPlus,
onStepDelete, onStepDelete,
messages,
}: Props) { }: Props) {
// sort steps by junction table's column // sort steps by junction table's column
const sortedSteps = steps.slice().sort((a, b) => { const sortedSteps = steps.slice().sort((a, b) => {
@@ -32,8 +34,7 @@ export default function StepsEditor({
<Textarea <Textarea
size="sm" size="sm"
variant="bordered" variant="bordered"
label="Details of the step" label={messages.detailsOfTheStep}
placeholder="Details of the step"
value={step.step} value={step.step}
onValueChange={(changeValue) => { onValueChange={(changeValue) => {
onStepUpdate(step.id, { ...step, step: changeValue }); onStepUpdate(step.id, { ...step, step: changeValue });
@@ -44,8 +45,7 @@ export default function StepsEditor({
<Textarea <Textarea
size="sm" size="sm"
variant="bordered" variant="bordered"
label="Expected Result" label={messages.expectedResult}
placeholder="Expected Result"
value={step.result} value={step.result}
onValueChange={(changeValue) => { onValueChange={(changeValue) => {
onStepUpdate(step.id, { ...step, result: changeValue }); onStepUpdate(step.id, { ...step, result: changeValue });
@@ -53,7 +53,7 @@ export default function StepsEditor({
className="mt-3 ms-1" className="mt-3 ms-1"
/> />
<div className="mt-3 ms-1"> <div className="mt-3 ms-1">
<Tooltip content="Delete this step" placement="left"> <Tooltip content={messages.deleteThisStep} placement="left">
<Button <Button
isIconOnly isIconOnly
size="sm" size="sm"
@@ -63,7 +63,7 @@ export default function StepsEditor({
<Trash size={16} /> <Trash size={16} />
</Button> </Button>
</Tooltip> </Tooltip>
<Tooltip content="Insert step" placement="left"> <Tooltip content={messages.insertStep} placement="left">
<Button <Button
isIconOnly isIconOnly
size="sm" size="sm"

View File

@@ -13,10 +13,51 @@ export default function Page({
}) { }) {
const t = useTranslations("Case"); const t = useTranslations("Case");
const messages = { const messages = {
backToCases: t("back_to_cases"),
updating: t("updating"),
update: t("update"),
basic: t("basic"),
title: t("title"),
pleaseEnterTitle: t("please_enter_title"),
description: t("description"),
testCaseDescription: t("test_case_description"),
priority: t("priority"),
critical: t("critical"), critical: t("critical"),
high: t("high"), high: t("high"),
medium: t("medium"), medium: t("medium"),
low: t("low"), low: t("low"),
type: t("type"),
other: t("other"),
security: t("security"),
performance: t("performance"),
accessibility: t("accessibility"),
functional: t("functional"),
acceptance: t("acceptance"),
usability: t("usability"),
smokeSanity: t("smoke_sanity"),
compatibility: t("compatibility"),
destructive: t("destructive"),
regression: t("regression"),
automated: t("automated"),
manual: t("manual"),
template: t("template"),
testDetail: t("test_detail"),
preconditions: t("preconditions"),
step: t("step"),
text: t("text"),
steps: t("steps"),
newStep: t("new_step"),
detailsOfTheStep: t("details_of_the_step"),
expectedResult: t("expected_result"),
deleteThisStep: t("delete_this_step"),
insertStep: t("insert_step"),
attachments: t("attachments"),
delete: t("delete"),
download: t("download"),
deleteFile: t("delete_file"),
clickToUpload: t("click_to_upload"),
orDragAndDrop: t("or_drag_and_drop"),
maxFileSize: t("max_file_size"),
}; };
return ( return (

View File

@@ -66,10 +66,51 @@ export type CasesMessages = {
}; };
export type CaseMessages = { export type CaseMessages = {
backToCases: string;
updating: string;
update: string;
basic: string;
title: string;
pleaseEnterTitle: string;
description: string;
testCaseDescription: string;
priority: string;
critical: string; critical: string;
high: string; high: string;
medium: string; medium: string;
low: string; low: string;
type: string;
other: string;
security: string;
performance: string;
accessibility: string;
functional: string;
acceptance: string;
usability: string;
smokeSanity: string;
compatibility: string;
destructive: string;
regression: string;
automated: string;
manual: string;
template: string;
testDetail: string;
preconditions: string;
step: string;
text: string;
steps: string;
newStep: string;
detailsOfTheStep: string;
expectedResult: string;
deleteThisStep: string;
insertStep: string;
attachments: string;
delete: string;
download: string;
deleteFile: string;
clickToUpload: string;
orDragAndDrop: string;
maxFileSize: string;
}; };
export { CaseType, StepType, AttachmentType, CasesMessages, CaseMessages }; export { CaseType, StepType, AttachmentType, CasesMessages, CaseMessages };