feat(i18n): add theme translation support for multiple languages

Add theme-related translations (toggle, light, dark, system) to JSON files for all supported languages. Update the mode-toggle component to use these translations for theme switching. Also, refactor API route files to improve code consistency and readability.
This commit is contained in:
Daniel Luiz Alves
2025-04-16 11:17:48 -03:00
parent 78e2d05d6c
commit d25f493c7a
29 changed files with 273 additions and 200 deletions

View File

@@ -545,6 +545,12 @@
"used": "المستخدمة",
"available": "المتاحة"
},
"theme": {
"toggle": "تبديل السمة",
"light": "فاتح",
"dark": "داكن",
"system": "النظام"
},
"uploadFile": {
"title": "رفع الملف",
"selectFile": "اضغط لاختيار ملف",

View File

@@ -545,6 +545,12 @@
"used": "genutzt",
"available": "verfügbar"
},
"theme": {
"toggle": "Design umschalten",
"light": "Hell",
"dark": "Dunkel",
"system": "System"
},
"uploadFile": {
"title": "Datei hochladen",
"selectFile": "Klicken Sie, um eine Datei auszuwählen",

View File

@@ -545,6 +545,12 @@
"used": "used",
"available": "available"
},
"theme": {
"toggle": "Toggle theme",
"light": "Light",
"dark": "Dark",
"system": "System"
},
"uploadFile": {
"title": "Upload File",
"selectFile": "Click to select a file",

View File

@@ -545,6 +545,12 @@
"used": "usados",
"available": "disponibles"
},
"theme": {
"toggle": "Cambiar tema",
"light": "Claro",
"dark": "Oscuro",
"system": "Sistema"
},
"uploadFile": {
"title": "Subir archivo",
"selectFile": "Haz clic para seleccionar un archivo",

View File

@@ -544,6 +544,12 @@
"used": "utilisé",
"available": "disponible"
},
"theme": {
"toggle": "Changer le thème",
"light": "Clair",
"dark": "Sombre",
"system": "Système"
},
"uploadFile": {
"title": "Envoyer un Fichier",
"selectFile": "Cliquez pour sélectionner un fichier",

View File

@@ -545,6 +545,12 @@
"used": "उपयोग किया गया",
"available": "उपलब्ध"
},
"theme": {
"toggle": "थीम टॉगल करें",
"light": "लाइट",
"dark": "डार्क",
"system": "सिस्टम"
},
"uploadFile": {
"title": "फाइल अपलोड करें",
"selectFile": "फाइल चुनने के लिए क्लिक करें",

View File

@@ -545,6 +545,12 @@
"used": "使用済み",
"available": "利用可能"
},
"theme": {
"toggle": "テーマを切り替える",
"light": "ライト",
"dark": "ダーク",
"system": "システム"
},
"uploadFile": {
"title": "ファイルをアップロード",
"selectFile": "ファイルを選択するにはクリックしてください",

View File

@@ -545,6 +545,12 @@
"used": "사용됨",
"available": "사용 가능"
},
"theme": {
"toggle": "테마 전환",
"light": "라이트",
"dark": "다크",
"system": "시스템"
},
"uploadFile": {
"title": "파일 업로드",
"selectFile": "파일 선택을 위해 클릭하세요",

View File

@@ -545,6 +545,12 @@
"used": "usado",
"available": "disponível"
},
"theme": {
"toggle": "Alternar tema",
"light": "Claro",
"dark": "Escuro",
"system": "Sistema"
},
"uploadFile": {
"title": "Enviar Arquivo",
"selectFile": "Clique para selecionar um arquivo",

View File

@@ -545,6 +545,12 @@
"used": "Использовано",
"available": "Доступно"
},
"theme": {
"toggle": "Переключить тему",
"light": "Светлая",
"dark": "Тёмная",
"system": "Системная"
},
"uploadFile": {
"title": "Загрузить файл",
"selectFile": "Нажмите, чтобы выбрать файл",

View File

@@ -545,6 +545,12 @@
"used": "kullanıldı",
"available": "kullanılabilir"
},
"theme": {
"toggle": "Temayı değiştir",
"light": "Açık",
"dark": "Koyu",
"system": "Sistem"
},
"uploadFile": {
"title": "Dosya Yükle",
"selectFile": "Dosya seçmek için tıklayın",

View File

@@ -545,6 +545,12 @@
"used": "已使用:",
"available": "可用:"
},
"theme": {
"toggle": "切换主题",
"light": "明亮",
"dark": "暗黑",
"system": "系统"
},
"uploadFile": {
"title": "上传文件",
"selectFile": "点击选择文件",

View File

@@ -1,7 +1,8 @@
import { NextRequest, NextResponse } from "next/server";
export async function GET(req: NextRequest, { params }: { params: { objectName: string } }) {
const { objectName } = params;
// Await params before destructuring
const { objectName } = await params;
const cookieHeader = req.headers.get("cookie");
const url = `${process.env.API_BASE_URL}/files/${encodeURIComponent(objectName)}/download`;

View File

@@ -1,32 +1,32 @@
import { NextRequest, NextResponse } from 'next/server'
import { NextRequest, NextResponse } from "next/server";
export async function POST(req: NextRequest, { params }: { params: { shareId: string } }) {
const body = await req.text()
const cookieHeader = req.headers.get('cookie')
const body = await req.text();
const cookieHeader = req.headers.get("cookie");
const apiRes = await fetch(`${process.env.API_BASE_URL}/shares/${params.shareId}/alias`, {
method: 'POST',
method: "POST",
headers: {
'Content-Type': 'application/json',
cookie: cookieHeader || '',
"Content-Type": "application/json",
cookie: cookieHeader || "",
},
body,
redirect: 'manual',
})
redirect: "manual",
});
const resBody = await apiRes.text()
const resBody = await apiRes.text();
const res = new NextResponse(resBody, {
status: apiRes.status,
headers: {
'Content-Type': 'application/json',
"Content-Type": "application/json",
},
})
});
const setCookie = apiRes.headers.getSetCookie?.() || []
const setCookie = apiRes.headers.getSetCookie?.() || [];
if (setCookie.length > 0) {
res.headers.set('Set-Cookie', setCookie.join(','))
res.headers.set("Set-Cookie", setCookie.join(","));
}
return res
}
return res;
}

View File

@@ -1,32 +1,32 @@
import { NextRequest, NextResponse } from 'next/server'
import { NextRequest, NextResponse } from "next/server";
export async function GET(req: NextRequest, { params }: { params: { alias: string } }) {
const cookieHeader = req.headers.get('cookie')
const queryParams = new URLSearchParams(req.url.split('?')[1]) || undefined
const cookieHeader = req.headers.get("cookie");
const queryParams = new URLSearchParams(req.url.split("?")[1]) || undefined;
const apiRes = await fetch(`${process.env.API_BASE_URL}/shares/alias/${params.alias}`, {
method: 'GET',
method: "GET",
headers: {
'Content-Type': 'application/json',
cookie: cookieHeader || '',
"Content-Type": "application/json",
cookie: cookieHeader || "",
},
redirect: 'manual',
redirect: "manual",
...(queryParams ? { params: queryParams } : {}),
})
});
const resBody = await apiRes.text()
const resBody = await apiRes.text();
const res = new NextResponse(resBody, {
status: apiRes.status,
headers: {
'Content-Type': 'application/json',
"Content-Type": "application/json",
},
})
});
const setCookie = apiRes.headers.getSetCookie?.() || []
const setCookie = apiRes.headers.getSetCookie?.() || [];
if (setCookie.length > 0) {
res.headers.set('Set-Cookie', setCookie.join(','))
res.headers.set("Set-Cookie", setCookie.join(","));
}
return res
}
return res;
}

View File

@@ -1,32 +1,32 @@
import { NextRequest, NextResponse } from 'next/server'
import { NextRequest, NextResponse } from "next/server";
export async function POST(req: NextRequest) {
const cookieHeader = req.headers.get('cookie')
const body = await req.text()
const cookieHeader = req.headers.get("cookie");
const body = await req.text();
const apiRes = await fetch(`${process.env.API_BASE_URL}/shares`, {
method: 'POST',
method: "POST",
headers: {
'Content-Type': 'application/json',
cookie: cookieHeader || '',
"Content-Type": "application/json",
cookie: cookieHeader || "",
},
body,
redirect: 'manual',
})
redirect: "manual",
});
const resBody = await apiRes.text()
const resBody = await apiRes.text();
const res = new NextResponse(resBody, {
status: apiRes.status,
headers: {
'Content-Type': 'application/json',
"Content-Type": "application/json",
},
})
});
const setCookie = apiRes.headers.getSetCookie?.() || []
const setCookie = apiRes.headers.getSetCookie?.() || [];
if (setCookie.length > 0) {
res.headers.set('Set-Cookie', setCookie.join(','))
res.headers.set("Set-Cookie", setCookie.join(","));
}
return res
}
return res;
}

View File

@@ -1,29 +1,29 @@
import { NextRequest, NextResponse } from 'next/server'
import { NextRequest, NextResponse } from "next/server";
export async function DELETE(req: NextRequest, { params }: { params: { id: string } }) {
const cookieHeader = req.headers.get('cookie')
const cookieHeader = req.headers.get("cookie");
const apiRes = await fetch(`${process.env.API_BASE_URL}/shares/${params.id}`, {
method: 'DELETE',
method: "DELETE",
headers: {
cookie: cookieHeader || '',
cookie: cookieHeader || "",
},
redirect: 'manual',
})
redirect: "manual",
});
const resBody = await apiRes.text()
const resBody = await apiRes.text();
const res = new NextResponse(resBody, {
status: apiRes.status,
headers: {
'Content-Type': 'application/json',
"Content-Type": "application/json",
},
})
});
const setCookie = apiRes.headers.getSetCookie?.() || []
const setCookie = apiRes.headers.getSetCookie?.() || [];
if (setCookie.length > 0) {
res.headers.set('Set-Cookie', setCookie.join(','))
res.headers.set("Set-Cookie", setCookie.join(","));
}
return res
}
return res;
}

View File

@@ -1,34 +1,34 @@
import { NextRequest, NextResponse } from 'next/server'
import { NextRequest, NextResponse } from "next/server";
export async function GET(req: NextRequest, { params }: { params: { shareId: string } }) {
const cookieHeader = req.headers.get('cookie')
const url = new URL(req.url)
const searchParams = url.searchParams.toString()
const cookieHeader = req.headers.get("cookie");
const url = new URL(req.url);
const searchParams = url.searchParams.toString();
const apiRes = await fetch(
`${process.env.API_BASE_URL}/shares/${params.shareId}${searchParams ? `?${searchParams}` : ''}`,
`${process.env.API_BASE_URL}/shares/${params.shareId}${searchParams ? `?${searchParams}` : ""}`,
{
method: 'GET',
method: "GET",
headers: {
cookie: cookieHeader || '',
cookie: cookieHeader || "",
},
redirect: 'manual',
redirect: "manual",
}
)
);
const resBody = await apiRes.text()
const resBody = await apiRes.text();
const res = new NextResponse(resBody, {
status: apiRes.status,
headers: {
'Content-Type': 'application/json',
"Content-Type": "application/json",
},
})
});
const setCookie = apiRes.headers.getSetCookie?.() || []
const setCookie = apiRes.headers.getSetCookie?.() || [];
if (setCookie.length > 0) {
res.headers.set('Set-Cookie', setCookie.join(','))
res.headers.set("Set-Cookie", setCookie.join(","));
}
return res
}
return res;
}

View File

@@ -1,32 +1,32 @@
import { NextRequest, NextResponse } from 'next/server'
import { NextRequest, NextResponse } from "next/server";
export async function POST(req: NextRequest, { params }: { params: { shareId: string } }) {
const body = await req.text()
const cookieHeader = req.headers.get('cookie')
const body = await req.text();
const cookieHeader = req.headers.get("cookie");
const apiRes = await fetch(`${process.env.API_BASE_URL}/shares/${params.shareId}/files`, {
method: 'POST',
method: "POST",
headers: {
'Content-Type': 'application/json',
cookie: cookieHeader || '',
"Content-Type": "application/json",
cookie: cookieHeader || "",
},
body,
redirect: 'manual',
})
redirect: "manual",
});
const resBody = await apiRes.text()
const resBody = await apiRes.text();
const res = new NextResponse(resBody, {
status: apiRes.status,
headers: {
'Content-Type': 'application/json',
"Content-Type": "application/json",
},
})
});
const setCookie = apiRes.headers.getSetCookie?.() || []
const setCookie = apiRes.headers.getSetCookie?.() || [];
if (setCookie.length > 0) {
res.headers.set('Set-Cookie', setCookie.join(','))
res.headers.set("Set-Cookie", setCookie.join(","));
}
return res
}
return res;
}

View File

@@ -1,32 +1,32 @@
import { NextRequest, NextResponse } from 'next/server'
import { NextRequest, NextResponse } from "next/server";
export async function DELETE(req: NextRequest, { params }: { params: { shareId: string } }) {
const body = await req.text()
const cookieHeader = req.headers.get('cookie')
const body = await req.text();
const cookieHeader = req.headers.get("cookie");
const apiRes = await fetch(`${process.env.API_BASE_URL}/shares/${params.shareId}/files`, {
method: 'DELETE',
method: "DELETE",
headers: {
'Content-Type': 'application/json',
cookie: cookieHeader || '',
"Content-Type": "application/json",
cookie: cookieHeader || "",
},
body,
redirect: 'manual',
})
redirect: "manual",
});
const resBody = await apiRes.text()
const resBody = await apiRes.text();
const res = new NextResponse(resBody, {
status: apiRes.status,
headers: {
'Content-Type': 'application/json',
"Content-Type": "application/json",
},
})
});
const setCookie = apiRes.headers.getSetCookie?.() || []
const setCookie = apiRes.headers.getSetCookie?.() || [];
if (setCookie.length > 0) {
res.headers.set('Set-Cookie', setCookie.join(','))
res.headers.set("Set-Cookie", setCookie.join(","));
}
return res
}
return res;
}

View File

@@ -1,29 +1,29 @@
import { NextRequest, NextResponse } from 'next/server'
import { NextRequest, NextResponse } from "next/server";
export async function GET(req: NextRequest) {
const cookieHeader = req.headers.get('cookie')
const cookieHeader = req.headers.get("cookie");
const apiRes = await fetch(`${process.env.API_BASE_URL}/shares/me`, {
method: 'GET',
method: "GET",
headers: {
cookie: cookieHeader || '',
cookie: cookieHeader || "",
},
redirect: 'manual',
})
redirect: "manual",
});
const resBody = await apiRes.text()
const resBody = await apiRes.text();
const res = new NextResponse(resBody, {
status: apiRes.status,
headers: {
'Content-Type': 'application/json',
"Content-Type": "application/json",
},
})
});
const setCookie = apiRes.headers.getSetCookie?.() || []
const setCookie = apiRes.headers.getSetCookie?.() || [];
if (setCookie.length > 0) {
res.headers.set('Set-Cookie', setCookie.join(','))
res.headers.set("Set-Cookie", setCookie.join(","));
}
return res
}
return res;
}

View File

@@ -1,32 +1,32 @@
import { NextRequest, NextResponse } from 'next/server'
import { NextRequest, NextResponse } from "next/server";
export async function PATCH(req: NextRequest, { params }: { params: { shareId: string } }) {
const body = await req.text()
const cookieHeader = req.headers.get('cookie')
const body = await req.text();
const cookieHeader = req.headers.get("cookie");
const apiRes = await fetch(`${process.env.API_BASE_URL}/shares/${params.shareId}/password`, {
method: 'PATCH',
method: "PATCH",
headers: {
'Content-Type': 'application/json',
cookie: cookieHeader || '',
"Content-Type": "application/json",
cookie: cookieHeader || "",
},
body,
redirect: 'manual',
})
redirect: "manual",
});
const resBody = await apiRes.text()
const resBody = await apiRes.text();
const res = new NextResponse(resBody, {
status: apiRes.status,
headers: {
'Content-Type': 'application/json',
"Content-Type": "application/json",
},
})
});
const setCookie = apiRes.headers.getSetCookie?.() || []
const setCookie = apiRes.headers.getSetCookie?.() || [];
if (setCookie.length > 0) {
res.headers.set('Set-Cookie', setCookie.join(','))
res.headers.set("Set-Cookie", setCookie.join(","));
}
return res
}
return res;
}

View File

@@ -1,32 +1,32 @@
import { NextRequest, NextResponse } from 'next/server'
import { NextRequest, NextResponse } from "next/server";
export async function POST(req: NextRequest, { params }: { params: { shareId: string } }) {
const body = await req.text()
const cookieHeader = req.headers.get('cookie')
const body = await req.text();
const cookieHeader = req.headers.get("cookie");
const apiRes = await fetch(`${process.env.API_BASE_URL}/shares/${params.shareId}/recipients`, {
method: 'POST',
method: "POST",
headers: {
'Content-Type': 'application/json',
cookie: cookieHeader || '',
"Content-Type": "application/json",
cookie: cookieHeader || "",
},
body,
redirect: 'manual',
})
redirect: "manual",
});
const resBody = await apiRes.text()
const resBody = await apiRes.text();
const res = new NextResponse(resBody, {
status: apiRes.status,
headers: {
'Content-Type': 'application/json',
"Content-Type": "application/json",
},
})
});
const setCookie = apiRes.headers.getSetCookie?.() || []
const setCookie = apiRes.headers.getSetCookie?.() || [];
if (setCookie.length > 0) {
res.headers.set('Set-Cookie', setCookie.join(','))
res.headers.set("Set-Cookie", setCookie.join(","));
}
return res
}
return res;
}

View File

@@ -1,32 +1,32 @@
import { NextRequest, NextResponse } from 'next/server'
import { NextRequest, NextResponse } from "next/server";
export async function POST(req: NextRequest, { params }: { params: { shareId: string } }) {
const body = await req.text()
const cookieHeader = req.headers.get('cookie')
const body = await req.text();
const cookieHeader = req.headers.get("cookie");
const apiRes = await fetch(`${process.env.API_BASE_URL}/shares/${params.shareId}/notify`, {
method: 'POST',
method: "POST",
headers: {
'Content-Type': 'application/json',
cookie: cookieHeader || '',
"Content-Type": "application/json",
cookie: cookieHeader || "",
},
body,
redirect: 'manual',
})
redirect: "manual",
});
const resBody = await apiRes.text()
const resBody = await apiRes.text();
const res = new NextResponse(resBody, {
status: apiRes.status,
headers: {
'Content-Type': 'application/json',
"Content-Type": "application/json",
},
})
});
const setCookie = apiRes.headers.getSetCookie?.() || []
const setCookie = apiRes.headers.getSetCookie?.() || [];
if (setCookie.length > 0) {
res.headers.set('Set-Cookie', setCookie.join(','))
res.headers.set("Set-Cookie", setCookie.join(","));
}
return res
}
return res;
}

View File

@@ -1,32 +1,32 @@
import { NextRequest, NextResponse } from 'next/server'
import { NextRequest, NextResponse } from "next/server";
export async function DELETE(req: NextRequest, { params }: { params: { shareId: string } }) {
const body = await req.text()
const cookieHeader = req.headers.get('cookie')
const body = await req.text();
const cookieHeader = req.headers.get("cookie");
const apiRes = await fetch(`${process.env.API_BASE_URL}/shares/${params.shareId}/recipients`, {
method: 'DELETE',
method: "DELETE",
headers: {
'Content-Type': 'application/json',
cookie: cookieHeader || '',
"Content-Type": "application/json",
cookie: cookieHeader || "",
},
body,
redirect: 'manual',
})
redirect: "manual",
});
const resBody = await apiRes.text()
const resBody = await apiRes.text();
const res = new NextResponse(resBody, {
status: apiRes.status,
headers: {
'Content-Type': 'application/json',
"Content-Type": "application/json",
},
})
});
const setCookie = apiRes.headers.getSetCookie?.() || []
const setCookie = apiRes.headers.getSetCookie?.() || [];
if (setCookie.length > 0) {
res.headers.set('Set-Cookie', setCookie.join(','))
res.headers.set("Set-Cookie", setCookie.join(","));
}
return res
}
return res;
}

View File

@@ -1,32 +1,32 @@
import { NextRequest, NextResponse } from 'next/server'
import { NextRequest, NextResponse } from "next/server";
export async function PUT(req: NextRequest) {
const cookieHeader = req.headers.get('cookie')
const body = await req.text()
const cookieHeader = req.headers.get("cookie");
const body = await req.text();
const apiRes = await fetch(`${process.env.API_BASE_URL}/shares`, {
method: 'PUT',
method: "PUT",
headers: {
'Content-Type': 'application/json',
cookie: cookieHeader || '',
"Content-Type": "application/json",
cookie: cookieHeader || "",
},
body,
redirect: 'manual',
})
redirect: "manual",
});
const resBody = await apiRes.text()
const resBody = await apiRes.text();
const res = new NextResponse(resBody, {
status: apiRes.status,
headers: {
'Content-Type': 'application/json',
"Content-Type": "application/json",
},
})
});
const setCookie = apiRes.headers.getSetCookie?.() || []
const setCookie = apiRes.headers.getSetCookie?.() || [];
if (setCookie.length > 0) {
res.headers.set('Set-Cookie', setCookie.join(','))
res.headers.set("Set-Cookie", setCookie.join(","));
}
return res
}
return res;
}

View File

@@ -23,7 +23,6 @@ const geistMono = Geist_Mono({
subsets: ["latin"],
});
export default async function RootLayout({
children,
}: Readonly<{

View File

@@ -1,8 +1,8 @@
// !!TODO: ADD TRANSLATION SUPPORT FOR THESE BUTTONS
"use client";
import * as React from "react";
import { IconMoon, IconSun } from "@tabler/icons-react";
import { useTranslations } from "next-intl";
import { useTheme } from "next-themes";
import { Button } from "@/components/ui/button";
@@ -15,6 +15,7 @@ import {
export function ModeToggle() {
const { setTheme } = useTheme();
const t = useTranslations();
return (
<DropdownMenu>
@@ -22,13 +23,13 @@ export function ModeToggle() {
<Button variant="ghost" size="icon">
<IconSun className="h-4 w-4 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
<IconMoon className="absolute h-4 w-4 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
<span className="sr-only">Toggle theme</span>
<span className="sr-only">{t("theme.toggle")}</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => setTheme("light")}>Light</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("dark")}>Dark</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("system")}>System</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("light")}>{t("theme.light")}</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("dark")}>{t("theme.dark")}</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("system")}>{t("theme.system")}</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);

View File

@@ -1,5 +1,6 @@
import type { AxiosRequestConfig } from "axios";
import apiInstance from "@/config/api";
import type {
AddFilesBody,
AddFilesResult,
@@ -26,7 +27,6 @@ import type {
UpdateSharePasswordResult,
UpdateShareResult,
} from "./types";
import apiInstance from "@/config/api";
/**
* Create a new share