Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(i18n): add i18n support #665

Merged
merged 12 commits into from
Nov 17, 2024
19 changes: 16 additions & 3 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ jobs:
docker login -u $DOCKERHUB_USERNAME -p $DOCKERHUB_TOKEN
if [ "${CIRCLE_BRANCH}" == "main" ]; then
TAG="latest"
else
elif [ "${CIRCLE_BRANCH}" == "canary" ]; then
TAG="canary"
else
TAG="feature"
fi
docker build --platform linux/amd64 -t dokploy/dokploy:${TAG}-amd64 .
docker push dokploy/dokploy:${TAG}-amd64
Expand All @@ -41,8 +43,10 @@ jobs:
docker login -u $DOCKERHUB_USERNAME -p $DOCKERHUB_TOKEN
if [ "${CIRCLE_BRANCH}" == "main" ]; then
TAG="latest"
else
elif [ "${CIRCLE_BRANCH}" == "canary" ]; then
TAG="canary"
else
TAG="feature"
fi
docker build --platform linux/arm64 -t dokploy/dokploy:${TAG}-arm64 .
docker push dokploy/dokploy:${TAG}-arm64
Expand Down Expand Up @@ -72,12 +76,18 @@ jobs:
dokploy/dokploy:${TAG}-amd64 \
dokploy/dokploy:${TAG}-arm64
docker manifest push dokploy/dokploy:${VERSION}
else
elif [ "${CIRCLE_BRANCH}" == "canary" ]; then
TAG="canary"
docker manifest create dokploy/dokploy:${TAG} \
dokploy/dokploy:${TAG}-amd64 \
dokploy/dokploy:${TAG}-arm64
docker manifest push dokploy/dokploy:${TAG}
else
TAG="feature"
docker manifest create dokploy/dokploy:${TAG} \
dokploy/dokploy:${TAG}-amd64 \
dokploy/dokploy:${TAG}-arm64
docker manifest push dokploy/dokploy:${TAG}
fi

workflows:
Expand All @@ -89,12 +99,14 @@ workflows:
only:
- main
- canary
- pull/665
- build-arm64:
filters:
branches:
only:
- main
- canary
- pull/665
- combine-manifests:
requires:
- build-amd64
Expand All @@ -104,3 +116,4 @@ workflows:
only:
- main
- canary
- pull/665
4 changes: 3 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@ We have a few guidelines to follow when contributing to this project:

## Commit Convention


Before you create a Pull Request, please make sure your commit message follows the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) specification.

### Commit Message Format


```
<type>[optional scope]: <description>

Expand Down Expand Up @@ -235,7 +237,7 @@ export function generate(schema: Schema): Template {

5. Add the logo or image of the template to `public/templates/plausible.svg`

### Recomendations
### Recommendations

- Use the same name of the folder as the id of the template.
- The logo should be in the public folder.
Expand Down
74 changes: 65 additions & 9 deletions apps/dokploy/components/dashboard/settings/appearance-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ import {
FormMessage,
} from "@/components/ui/form";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import useLocale from "@/utils/hooks/use-locale";
import { useTranslation } from "next-i18next";
import { useTheme } from "next-themes";
import { useEffect } from "react";
import { toast } from "sonner";
Expand All @@ -28,17 +37,24 @@ const appearanceFormSchema = z.object({
theme: z.enum(["light", "dark", "system"], {
required_error: "Please select a theme.",
}),
language: z.enum(["en", "zh-Hans"], {
required_error: "Please select a language.",
}),
});

type AppearanceFormValues = z.infer<typeof appearanceFormSchema>;

// This can come from your database or API.
const defaultValues: Partial<AppearanceFormValues> = {
theme: "system",
language: "en",
};

export function AppearanceForm() {
const { setTheme, theme } = useTheme();
const { locale, setLocale } = useLocale();
const { t } = useTranslation("settings");

const form = useForm<AppearanceFormValues>({
resolver: zodResolver(appearanceFormSchema),
defaultValues,
Expand All @@ -47,19 +63,23 @@ export function AppearanceForm() {
useEffect(() => {
form.reset({
theme: (theme ?? "system") as AppearanceFormValues["theme"],
language: locale,
});
}, [form, theme]);
}, [form, theme, locale]);
function onSubmit(data: AppearanceFormValues) {
setTheme(data.theme);
setLocale(data.language);
toast.success("Preferences Updated");
}

return (
<Card className="bg-transparent">
<CardHeader>
<CardTitle className="text-xl">Appearance</CardTitle>
<CardTitle className="text-xl">
{t("settings.appearance.title")}
</CardTitle>
<CardDescription>
Customize the theme of your dashboard.
{t("settings.appearance.description")}
</CardDescription>
</CardHeader>
<CardContent className="space-y-2">
Expand All @@ -72,9 +92,9 @@ export function AppearanceForm() {
render={({ field }) => {
return (
<FormItem className="space-y-1 ">
<FormLabel>Theme</FormLabel>
<FormLabel>{t("settings.appearance.theme")}</FormLabel>
<FormDescription>
Select a theme for your dashboard
{t("settings.appearance.themeDescription")}
</FormDescription>
<FormMessage />
<RadioGroup
Expand All @@ -92,7 +112,7 @@ export function AppearanceForm() {
<img src="/images/theme-light.svg" alt="light" />
</div>
<span className="block w-full p-2 text-center font-normal">
Light
{t("settings.appearance.themes.light")}
</span>
</FormLabel>
</FormItem>
Expand All @@ -105,7 +125,7 @@ export function AppearanceForm() {
<img src="/images/theme-dark.svg" alt="dark" />
</div>
<span className="block w-full p-2 text-center font-normal">
Dark
{t("settings.appearance.themes.dark")}
</span>
</FormLabel>
</FormItem>
Expand All @@ -121,7 +141,7 @@ export function AppearanceForm() {
<img src="/images/theme-system.svg" alt="system" />
</div>
<span className="block w-full p-2 text-center font-normal">
System
{t("settings.appearance.themes.system")}
</span>
</FormLabel>
</FormItem>
Expand All @@ -131,7 +151,43 @@ export function AppearanceForm() {
}}
/>

<Button type="submit">Save</Button>
<FormField
control={form.control}
name="language"
defaultValue={form.control._defaultValues.language}
render={({ field }) => {
return (
<FormItem className="space-y-1">
<FormLabel>{t("settings.appearance.language")}</FormLabel>
<FormDescription>
{t("settings.appearance.languageDescription")}
</FormDescription>
<FormMessage />
<Select
onValueChange={field.onChange}
defaultValue={field.value}
value={field.value}
>
<SelectTrigger>
<SelectValue placeholder="No preset selected" />
</SelectTrigger>
<SelectContent>
{[
{ label: "English", value: "en" },
{ label: "简体中文", value: "zh-Hans" },
].map((preset) => (
<SelectItem key={preset.label} value={preset.value}>
{preset.label}
</SelectItem>
))}
</SelectContent>
</Select>
</FormItem>
);
}}
/>

<Button type="submit">{t("settings.common.save")}</Button>
</form>
</Form>
</CardContent>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { Input } from "@/components/ui/input";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { api } from "@/utils/api";
import { zodResolver } from "@hookform/resolvers/zod";
import { useTranslation } from "next-i18next";
import { useEffect } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
Expand Down Expand Up @@ -51,6 +52,7 @@ const randomImages = [
export const ProfileForm = () => {
const { data, refetch } = api.auth.get.useQuery();
const { mutateAsync, isLoading } = api.auth.update.useMutation();
const { t } = useTranslation("settings");

const form = useForm<Profile>({
defaultValues: {
Expand Down Expand Up @@ -91,10 +93,10 @@ export const ProfileForm = () => {
<Card className="bg-transparent">
<CardHeader className="flex flex-row gap-2 flex-wrap justify-between items-center">
<div>
<CardTitle className="text-xl">Account</CardTitle>
<CardDescription>
Change the details of your profile here.
</CardDescription>
<CardTitle className="text-xl">
{t("settings.profile.title")}
</CardTitle>
<CardDescription>{t("settings.profile.description")}</CardDescription>
</div>
{!data?.is2FAEnabled ? <Enable2FA /> : <Disable2FA />}
</CardHeader>
Expand All @@ -107,9 +109,12 @@ export const ProfileForm = () => {
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormLabel>{t("settings.profile.email")}</FormLabel>
<FormControl>
<Input placeholder="Email" {...field} />
<Input
placeholder={t("settings.profile.email")}
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
Expand All @@ -120,11 +125,11 @@ export const ProfileForm = () => {
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<FormLabel>{t("settings.profile.password")}</FormLabel>
<FormControl>
<Input
type="password"
placeholder="Password"
placeholder={t("settings.profile.password")}
{...field}
value={field.value || ""}
/>
Expand All @@ -139,7 +144,7 @@ export const ProfileForm = () => {
name="image"
render={({ field }) => (
<FormItem>
<FormLabel>Avatar</FormLabel>
<FormLabel>{t("settings.profile.avatar")}</FormLabel>
<FormControl>
<RadioGroup
onValueChange={(e) => {
Expand Down Expand Up @@ -177,7 +182,7 @@ export const ProfileForm = () => {
</div>
<div>
<Button type="submit" isLoading={isLoading}>
Save
{t("settings.common.save")}
</Button>
</div>
</form>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,26 @@ import {
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { api } from "@/utils/api";
import { useTranslation } from "next-i18next";
import { toast } from "sonner";
import { ShowModalLogs } from "../../web-server/show-modal-logs";

export const ShowDokployActions = () => {
const { t } = useTranslation("settings");
const { mutateAsync: reloadServer, isLoading } =
api.settings.reloadServer.useMutation();

return (
<DropdownMenu>
<DropdownMenuTrigger asChild disabled={isLoading}>
<Button isLoading={isLoading} variant="outline">
Server
{t("settings.server.webServer.server.label")}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-56" align="start">
<DropdownMenuLabel>Actions</DropdownMenuLabel>
<DropdownMenuLabel>
{t("settings.server.webServer.actions")}
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem
Expand All @@ -40,10 +44,10 @@ export const ShowDokployActions = () => {
});
}}
>
<span>Reload</span>
<span>{t("settings.server.webServer.reload")}</span>
</DropdownMenuItem>
<ShowModalLogs appName="dokploy">
<span>Watch logs</span>
<span>{t("settings.server.webServer.watchLogs")}</span>
</ShowModalLogs>
</DropdownMenuGroup>
</DropdownMenuContent>
Expand Down
Loading