diff --git a/apps/with-lingui/lingui.config.ts b/apps/with-lingui/lingui.config.ts index 5f2cdfe..668b9ee 100644 --- a/apps/with-lingui/lingui.config.ts +++ b/apps/with-lingui/lingui.config.ts @@ -1,3 +1,5 @@ +import { formatter } from "@lingui/format-po"; + const config = { locales: ["en", "zh"], sourceLocale: "en", @@ -7,7 +9,7 @@ const config = { include: ["src"], }, ], - format: "po", + format: formatter({ lineNumbers: false }), }; export default config; diff --git a/apps/with-lingui/package.json b/apps/with-lingui/package.json index 5b7a660..3440a66 100644 --- a/apps/with-lingui/package.json +++ b/apps/with-lingui/package.json @@ -16,34 +16,36 @@ "test:e2e:core": "playwright test e2e/login.spec.ts e2e/users.spec.ts e2e/auth-refresh.spec.ts e2e/rbac.spec.ts e2e/url-state.spec.ts", "test:e2e:ui": "playwright test --ui", "i18n:extract": "vp exec lingui extract -- --clean", - "i18n:compile": "vp exec lingui compile" + "i18n:compile": "vp exec lingui compile", + "i18n:check": "pnpm run i18n:extract && git diff --exit-code -- src/locales" }, "dependencies": { - "@lingui/core": "^5.9.3", - "@lingui/react": "^5.9.3", - "@tanstack/react-query": "^5.90.21", - "@tanstack/react-router": "^1.167.4", - "antd": "^6.3.3", - "lucide-react": "^1.7.0", - "react": "^19.2.4", - "react-dom": "^19.2.4", + "@lingui/core": "^6.0.0", + "@lingui/react": "^6.0.0", + "@tanstack/react-query": "^5.100.5", + "@tanstack/react-router": "^1.168.25", + "antd": "^6.3.7", + "lucide-react": "^1.11.0", + "react": "^19.2.5", + "react-dom": "^19.2.5", "zod": "^4.3.6", "zustand": "^5.0.12" }, "devDependencies": { - "@lingui/cli": "^5.9.3", - "@lingui/swc-plugin": "^5.11.0", - "@lingui/vite-plugin": "^5.9.3", - "@playwright/test": "^1.58.2", - "@tanstack/router-plugin": "^1.166.13", + "@lingui/cli": "^6.0.0", + "@lingui/format-po": "^6.0.0", + "@lingui/swc-plugin": "^6.0.0", + "@lingui/vite-plugin": "^6.0.0", + "@playwright/test": "^1.59.1", + "@tanstack/router-plugin": "^1.167.28", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react-swc": "^4.3.0", - "msw": "^2.12.12", - "typescript": "~5.9.3", + "msw": "^2.13.6", + "typescript": "~6.0.3", "vite": "npm:@voidzero-dev/vite-plus-core@latest", "vite-plus": "latest", - "vitest": "3.0.5" + "vitest": "4.1.5" }, "packageManager": "pnpm@10.32.1", "pnpm": { diff --git a/apps/with-lingui/src/api/auth.ts b/apps/with-lingui/src/api/auth.ts index 7afbd41..2d4314e 100644 --- a/apps/with-lingui/src/api/auth.ts +++ b/apps/with-lingui/src/api/auth.ts @@ -1,11 +1,19 @@ -import type { AuthTokens, LoginRequest, User, MenuItem, PermissionsList } from "./schemas"; +import type { + AuthTokens, + LoginRequest, + User, + MenuItem, + PermissionsList, + RegisterRequest, +} from "./schemas"; export const AUTH_ENDPOINTS = { login: "/api/auth/login", + register: "/api/auth/register", refresh: "/api/auth/refresh", logout: "/api/auth/logout", user: "/api/auth/user", permissions: "/api/auth/permissions", } as const; -export type { AuthTokens, LoginRequest, User, MenuItem, PermissionsList }; +export type { AuthTokens, LoginRequest, User, MenuItem, PermissionsList, RegisterRequest }; diff --git a/apps/with-lingui/src/api/schemas.ts b/apps/with-lingui/src/api/schemas.ts index 265cd53..cbe72d4 100644 --- a/apps/with-lingui/src/api/schemas.ts +++ b/apps/with-lingui/src/api/schemas.ts @@ -34,6 +34,24 @@ export const LoginRequestSchema = z.object({ export type LoginRequest = z.infer; +/** Ant Design Form may submit `""` for an empty optional email */ +const registerOptionalEmailSchema = z + .union([z.string(), z.undefined()]) + .transform((v) => { + if (v === undefined) return undefined; + const t = String(v).trim(); + return t === "" ? undefined : t; + }) + .pipe(z.union([z.string().email(), z.undefined()])); + +export const RegisterRequestSchema = z.object({ + username: z.string().min(1), + password: z.string().min(6), + email: registerOptionalEmailSchema, +}); + +export type RegisterRequest = z.infer; + export const PermissionsListSchema = z.array(z.string()); export type PermissionsList = z.infer; diff --git a/apps/with-lingui/src/components/Layout/AppFooter/index.tsx b/apps/with-lingui/src/components/Layout/AppFooter/index.tsx index 9670f33..181d24f 100644 --- a/apps/with-lingui/src/components/Layout/AppFooter/index.tsx +++ b/apps/with-lingui/src/components/Layout/AppFooter/index.tsx @@ -1,41 +1,71 @@ -import { Flex, Typography, theme } from "antd"; +import { Button, Flex, Space, Typography, theme } from "antd"; import { useLingui } from "@lingui/react/macro"; +import { Languages } from "lucide-react"; +import { useSettingsStore } from "@/stores/settings"; import { GitHub } from "@/components/Icon"; +import { Theme } from "@/components/Icon"; const ANTD_ADMIN_REPO = "https://github.com/zuiidea/antd-admin"; export function AppFooter() { const { token } = theme.useToken(); const { t } = useLingui(); + const locale = useSettingsStore((s) => s.locale); + const setLocale = useSettingsStore((s) => s.setLocale); + const toggleDarkMode = useSettingsStore((s) => s.toggleDarkMode); const iconSize = Math.max(12, Math.round(Number(token.fontSizeSM))); + const toggleLocale = () => { + setLocale(locale === "en" ? "zh" : "en"); + }; + return ( - - - {t`Powered by`} - - + + + + + + {t`Don't have an account?`}{" "} + + {t`Sign up`} + + + - - - + + + + + {t`Already have an account?`}{" "} + + {t`Sign in`} + + + + + + + + + + + + ); +}