} />
+
+
+ ,
+ );
+}
+
+// The line under the email in the sidebar's user card.
+function roleLine(email: string) {
+ return screen.getByText(email).nextElementSibling?.textContent;
+}
+
+describe("AppShell user card", () => {
+ it.each([
+ ["en-US", "owner", "Owner"],
+ ["en-US", "user", "User"],
+ ["zh-CN", "admin", "管理员"],
+ ["zh-CN", "user", "普通用户"],
+ ] as const)("names the role in %s: %s is %s", async (lang, role, shown) => {
+ await i18next.changeLanguage(lang);
+ tier.identity = { user_id: "u-1", email: "a@example.test", role } as Identity;
+ renderShell();
+ expect(roleLine("a@example.test")).toBe(shown);
+ });
+
+ it("names no role before it has read one", () => {
+ renderShell();
+ const dashes = screen.getAllByText("—");
+ expect(dashes).toHaveLength(2);
+ expect(screen.queryByText(/^user$/i)).toBeNull();
+ });
+});
diff --git a/panel/src/components/AppShell.tsx b/panel/src/components/AppShell.tsx
index a416558..8fe7d08 100644
--- a/panel/src/components/AppShell.tsx
+++ b/panel/src/components/AppShell.tsx
@@ -14,6 +14,7 @@ import { Button } from "@/components/ui/button";
import { ConfigBanner, VersionBadge } from "@/components/RuntimeStatus";
import { ErrorBoundary } from "@/components/ErrorBoundary";
import { Loading } from "@/components/States";
+import { ROLE_LABEL_KEY } from "@/components/RoleBadge";
function SectionGroup({
section,
@@ -71,9 +72,9 @@ const FOOT_ICON_BTN =
function UserStrip() {
// The sidebar foot identifies the principal and exposes one action — sign out.
- // identity?.email is the only display-safe field (user_id is a UUID, role is
- // server-truth not display). While /me is loading or has failed we render a
- // muted placeholder rather than a broken row, so the strip never flashes empty.
+ // It shows the email and the role by its name in the UI language (user_id is a
+ // UUID). While /me is loading or has failed both lines hold a muted dash, so
+ // the strip never flashes empty and never names a role it has not read.
const { identity, refresh } = useTier();
const { t } = useTranslation("account");
const [signingOut, setSigningOut] = useState(false);
@@ -99,8 +100,8 @@ function UserStrip() {
>
{identity?.email ?? —}
-