chore: rebrand product as 省心推 and lock UI to zh-CN
Update server titles, web/desktop window/tray/document titles, login copy, and electron build metadata to use the Chinese product name 省心推. Drop the runtime locale switcher and en-US import — the app now ships zh-CN only. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,7 @@
|
|||||||
name="viewport"
|
name="viewport"
|
||||||
content="width=device-width, initial-scale=1.0"
|
content="width=device-width, initial-scale=1.0"
|
||||||
/>
|
/>
|
||||||
<title>GEO 工具平台</title>
|
<title>省心推</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|||||||
@@ -1,18 +1,12 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import enUS from "ant-design-vue/es/locale/en_US";
|
|
||||||
import zhCN from "ant-design-vue/es/locale/zh_CN";
|
import zhCN from "ant-design-vue/es/locale/zh_CN";
|
||||||
import { computed } from "vue";
|
|
||||||
import { useI18n } from "vue-i18n";
|
|
||||||
|
|
||||||
import ImageUploadProgressOverlay from "@/components/ImageUploadProgressOverlay.vue";
|
import ImageUploadProgressOverlay from "@/components/ImageUploadProgressOverlay.vue";
|
||||||
|
|
||||||
const { locale } = useI18n();
|
|
||||||
const antLocale = computed(() => (locale.value === "en-US" ? enUS : zhCN));
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<a-config-provider
|
<a-config-provider
|
||||||
:locale="antLocale"
|
:locale="zhCN"
|
||||||
:theme="{
|
:theme="{
|
||||||
token: {
|
token: {
|
||||||
colorPrimary: '#1677ff',
|
colorPrimary: '#1677ff',
|
||||||
|
|||||||
@@ -1,44 +1,14 @@
|
|||||||
import { createI18n } from "vue-i18n";
|
import { createI18n } from "vue-i18n";
|
||||||
|
|
||||||
import enUS from "./messages/en-US";
|
|
||||||
import zhCN from "./messages/zh-CN";
|
import zhCN from "./messages/zh-CN";
|
||||||
|
|
||||||
export const LOCALE_STORAGE_KEY = "geo.admin.locale";
|
const APP_LOCALE = "zh-CN";
|
||||||
export const SUPPORTED_LOCALES = ["zh-CN", "en-US"] as const;
|
|
||||||
|
|
||||||
export type AppLocale = (typeof SUPPORTED_LOCALES)[number];
|
|
||||||
|
|
||||||
function resolveLocale(): AppLocale {
|
|
||||||
if (typeof window === "undefined") {
|
|
||||||
return "zh-CN";
|
|
||||||
}
|
|
||||||
|
|
||||||
const stored = window.localStorage.getItem(LOCALE_STORAGE_KEY);
|
|
||||||
if (stored && SUPPORTED_LOCALES.includes(stored as AppLocale)) {
|
|
||||||
return stored as AppLocale;
|
|
||||||
}
|
|
||||||
|
|
||||||
const browserLocale = window.navigator.language;
|
|
||||||
if (browserLocale.startsWith("zh")) {
|
|
||||||
return "zh-CN";
|
|
||||||
}
|
|
||||||
|
|
||||||
return "en-US";
|
|
||||||
}
|
|
||||||
|
|
||||||
export const i18n = createI18n({
|
export const i18n = createI18n({
|
||||||
legacy: false,
|
legacy: false,
|
||||||
locale: resolveLocale(),
|
locale: APP_LOCALE,
|
||||||
fallbackLocale: "en-US",
|
fallbackLocale: APP_LOCALE,
|
||||||
messages: {
|
messages: {
|
||||||
"zh-CN": zhCN,
|
"zh-CN": zhCN,
|
||||||
"en-US": enUS,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export function setAppLocale(locale: AppLocale): void {
|
|
||||||
i18n.global.locale.value = locale;
|
|
||||||
if (typeof window !== "undefined") {
|
|
||||||
window.localStorage.setItem(LOCALE_STORAGE_KEY, locale);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
const zhCN = {
|
const zhCN = {
|
||||||
app: {
|
app: {
|
||||||
name: "GEO工具平台",
|
name: "省心推",
|
||||||
loginIntro: "面向企业的品牌资产管理与内容生产协作系统",
|
loginIntro: "面向企业和个人的品牌资产管理与内容生产协作系统",
|
||||||
},
|
},
|
||||||
locale: {
|
locale: {
|
||||||
"zh-CN": "中文简体",
|
"zh-CN": "中文简体",
|
||||||
@@ -153,7 +153,7 @@ const zhCN = {
|
|||||||
route: {
|
route: {
|
||||||
login: {
|
login: {
|
||||||
title: "欢迎回来",
|
title: "欢迎回来",
|
||||||
description: "使用真实租户账号登录 GEO 工具平台。",
|
description: "登录省心推,开启品牌内容创作之旅",
|
||||||
},
|
},
|
||||||
workspace: {
|
workspace: {
|
||||||
title: "工作台",
|
title: "工作台",
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import { useRoute, useRouter } from "vue-router";
|
|||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
|
|
||||||
import { workspaceApi } from "@/lib/api";
|
import { workspaceApi } from "@/lib/api";
|
||||||
import { type AppLocale, setAppLocale } from "@/i18n";
|
|
||||||
import { useAuthStore } from "@/stores/auth";
|
import { useAuthStore } from "@/stores/auth";
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
@@ -86,22 +85,6 @@ type NavSection = {
|
|||||||
items: NavItem[];
|
items: NavItem[];
|
||||||
};
|
};
|
||||||
|
|
||||||
const localeOptions = computed(() => [
|
|
||||||
{ label: t("locale.zh-CN"), value: "zh-CN" },
|
|
||||||
{ label: t("locale.en-US"), value: "en-US" },
|
|
||||||
]);
|
|
||||||
|
|
||||||
const currentLocale = computed({
|
|
||||||
get: () => locale.value as AppLocale,
|
|
||||||
set: (value: AppLocale) => {
|
|
||||||
setAppLocale(value);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const currentLocaleLabel = computed(() => {
|
|
||||||
return localeOptions.value.find((opt) => opt.value === currentLocale.value)?.label ?? "Language";
|
|
||||||
});
|
|
||||||
|
|
||||||
const navSections = computed<NavSection[]>(() => {
|
const navSections = computed<NavSection[]>(() => {
|
||||||
const base = [
|
const base = [
|
||||||
{
|
{
|
||||||
@@ -220,10 +203,6 @@ function go(path: string): void {
|
|||||||
void router.push(path);
|
void router.push(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleLocaleChange({ key }: { key: any }): void {
|
|
||||||
currentLocale.value = key as AppLocale;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleLogout(): Promise<void> {
|
async function handleLogout(): Promise<void> {
|
||||||
await authStore.logout();
|
await authStore.logout();
|
||||||
await router.replace("/login");
|
await router.replace("/login");
|
||||||
@@ -298,22 +277,6 @@ async function handleLogout(): Promise<void> {
|
|||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div class="admin-header-actions">
|
<div class="admin-header-actions">
|
||||||
<div v-if="!isMembershipBlocked" class="admin-locale-switch">
|
|
||||||
<a-dropdown placement="bottomRight" :trigger="['click', 'hover']">
|
|
||||||
<div class="locale-dropdown-trigger">
|
|
||||||
<GlobalOutlined class="locale-icon" />
|
|
||||||
<span>{{ currentLocaleLabel }}</span>
|
|
||||||
</div>
|
|
||||||
<template #overlay>
|
|
||||||
<a-menu :selected-keys="[currentLocale]" @click="handleLocaleChange">
|
|
||||||
<a-menu-item v-for="opt in localeOptions" :key="opt.value">
|
|
||||||
{{ opt.label }}
|
|
||||||
</a-menu-item>
|
|
||||||
</a-menu>
|
|
||||||
</template>
|
|
||||||
</a-dropdown>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Quota Indicator -->
|
<!-- Quota Indicator -->
|
||||||
<div v-if="!isMembershipBlocked" class="quota-pill">
|
<div v-if="!isMembershipBlocked" class="quota-pill">
|
||||||
<a-tag color="blue" :bordered="false" class="quota-pill-tag">{{ quotaSummary?.plan_name || t("shell.planFallback") }}</a-tag>
|
<a-tag color="blue" :bordered="false" class="quota-pill-tag">{{ quotaSummary?.plan_name || t("shell.planFallback") }}</a-tag>
|
||||||
@@ -487,37 +450,6 @@ async function handleLogout(): Promise<void> {
|
|||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-locale-switch {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-locale-select {
|
|
||||||
width: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.locale-dropdown-trigger {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 6px;
|
|
||||||
padding: 4px 10px;
|
|
||||||
height: 32px;
|
|
||||||
border-radius: 6px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.2s;
|
|
||||||
font-size: 14px;
|
|
||||||
color: #141414;
|
|
||||||
}
|
|
||||||
|
|
||||||
.locale-dropdown-trigger:hover {
|
|
||||||
background: #f5f5f5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.locale-icon {
|
|
||||||
font-size: 16px;
|
|
||||||
color: #595959;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quota-pill {
|
.quota-pill {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
appId: com.geo.rankly.desktop
|
appId: com.geo.rankly.desktop
|
||||||
productName: GEO Rankly Desktop
|
productName: 省心推
|
||||||
copyright: Copyright © 2026 GEO Rankly
|
copyright: Copyright © 2026 shengxintui.com. All Rights Reserved.
|
||||||
directories:
|
directories:
|
||||||
output: release
|
output: release
|
||||||
buildResources: build
|
buildResources: build
|
||||||
|
|||||||
@@ -2,10 +2,11 @@
|
|||||||
"name": "@geo/desktop-client",
|
"name": "@geo/desktop-client",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "GEO Rankly Desktop — account health, monitor scheduler, and publish dispatch client.",
|
"description": "省心推客户端 — 连接和管理您的媒体账号,轻松发布和监控内容表现。",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "GEO Rankly",
|
"name": "Liang Xu",
|
||||||
"email": "support@geo-rankly.local"
|
"email": "liangxu@qq.com",
|
||||||
|
"url": "https://shengxintui.com"
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "out/main/bootstrap.cjs",
|
"main": "out/main/bootstrap.cjs",
|
||||||
|
|||||||
@@ -340,7 +340,7 @@ async function createAppWindow(mode: WindowMode): Promise<ElectronBrowserWindow>
|
|||||||
minWidth: initial.minWidth,
|
minWidth: initial.minWidth,
|
||||||
minHeight: initial.minHeight,
|
minHeight: initial.minHeight,
|
||||||
show: false,
|
show: false,
|
||||||
title: "GEO Rankly Desktop",
|
title: "省心推",
|
||||||
icon: createDesktopHealthIcon("normal"),
|
icon: createDesktopHealthIcon("normal"),
|
||||||
titleBarStyle: "hiddenInset",
|
titleBarStyle: "hiddenInset",
|
||||||
trafficLightPosition: { x: 12, y: 14 },
|
trafficLightPosition: { x: 12, y: 14 },
|
||||||
@@ -414,7 +414,7 @@ async function createSettingsWindow(): Promise<ElectronBrowserWindow> {
|
|||||||
minWidth: initial.minWidth,
|
minWidth: initial.minWidth,
|
||||||
minHeight: initial.minHeight,
|
minHeight: initial.minHeight,
|
||||||
show: false,
|
show: false,
|
||||||
title: "GEO Rankly Desktop 设置",
|
title: "省心推设置",
|
||||||
icon: createDesktopHealthIcon("normal"),
|
icon: createDesktopHealthIcon("normal"),
|
||||||
titleBarStyle: "hiddenInset",
|
titleBarStyle: "hiddenInset",
|
||||||
trafficLightPosition: { x: 14, y: 15 },
|
trafficLightPosition: { x: 14, y: 15 },
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ describe("playwright cdp target pruning", () => {
|
|||||||
expect(isReclaimableHiddenPlaywrightTarget({
|
expect(isReclaimableHiddenPlaywrightTarget({
|
||||||
id: "renderer",
|
id: "renderer",
|
||||||
type: "page",
|
type: "page",
|
||||||
title: "GEO Rankly Desktop",
|
title: "省心推 - 账号健康监控与内容发布平台",
|
||||||
url: "http://localhost:5173/",
|
url: "http://localhost:5173/",
|
||||||
})).toBe(false);
|
})).toBe(false);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ function createLiveRuntimeSnapshot(controller: RuntimeControllerSnapshot) {
|
|||||||
return {
|
return {
|
||||||
generatedAt: now,
|
generatedAt: now,
|
||||||
app: {
|
app: {
|
||||||
name: "GEO Rankly Desktop",
|
name: "省心推",
|
||||||
version: app.getVersion(),
|
version: app.getVersion(),
|
||||||
channel: process.env.NODE_ENV === "development" ? "dev" : "release",
|
channel: process.env.NODE_ENV === "development" ? "dev" : "release",
|
||||||
platform: process.platform,
|
platform: process.platform,
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export function updateTrayIssueIndicator(issueCount: number): void {
|
|||||||
|
|
||||||
tray.setImage(createTrayIcon(hasIssues ? "danger" : "normal"));
|
tray.setImage(createTrayIcon(hasIssues ? "danger" : "normal"));
|
||||||
tray.setTitle(hasIssues ? formatIssueCount(safeCount) : "", { fontType: "monospacedDigit" });
|
tray.setTitle(hasIssues ? formatIssueCount(safeCount) : "", { fontType: "monospacedDigit" });
|
||||||
tray.setToolTip(hasIssues ? `GEO Rankly Desktop · ${safeCount} 个账号健康问题` : "GEO Rankly Desktop");
|
tray.setToolTip(hasIssues ? `省心推 · ${safeCount} 个账号健康问题` : "省心推");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function initTray(onOpen: () => void): ElectronTray {
|
export function initTray(onOpen: () => void): ElectronTray {
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ function openSettingsWindow() {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p class="eyebrow">Workspace Cockpit</p>
|
<p class="eyebrow">Workspace Cockpit</p>
|
||||||
<h1>GEO Rankly Desktop</h1>
|
<h1>省心推</h1>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
rel="icon"
|
rel="icon"
|
||||||
href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cdefs%3E%3ClinearGradient id='g' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop stop-color='%230f766e'/%3E%3Cstop offset='1' stop-color='%231b6cff'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='64' height='64' rx='18' fill='%23f5f7fb'/%3E%3Ccircle cx='32' cy='32' r='18' fill='none' stroke='url(%23g)' stroke-width='6'/%3E%3Ccircle cx='32' cy='32' r='8' fill='url(%23g)'/%3E%3C/svg%3E"
|
href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cdefs%3E%3ClinearGradient id='g' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop stop-color='%230f766e'/%3E%3Cstop offset='1' stop-color='%231b6cff'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='64' height='64' rx='18' fill='%23f5f7fb'/%3E%3Ccircle cx='32' cy='32' r='18' fill='none' stroke='url(%23g)' stroke-width='6'/%3E%3Ccircle cx='32' cy='32' r='8' fill='url(%23g)'/%3E%3C/svg%3E"
|
||||||
/>
|
/>
|
||||||
<title>GEO Rankly Desktop</title>
|
<title>省心推</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ onBeforeUnmount(() => {
|
|||||||
<div class="brand-logo">
|
<div class="brand-logo">
|
||||||
<span class="brand-core"></span>
|
<span class="brand-core"></span>
|
||||||
</div>
|
</div>
|
||||||
<h1 class="brand-name">GEO Rankly</h1>
|
<h1 class="brand-name">省心推</h1>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<form class="login-form" @submit.prevent="submitLogin">
|
<form class="login-form" @submit.prevent="submitLogin">
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ func BuildOpenAPI(routes gin.RoutesInfo, cfg Config) map[string]any {
|
|||||||
|
|
||||||
func withDefaults(cfg Config) Config {
|
func withDefaults(cfg Config) Config {
|
||||||
if strings.TrimSpace(cfg.Title) == "" {
|
if strings.TrimSpace(cfg.Title) == "" {
|
||||||
cfg.Title = "Geo Rankly Tenant API"
|
cfg.Title = "省心推 客户 API"
|
||||||
}
|
}
|
||||||
if strings.TrimSpace(cfg.Version) == "" {
|
if strings.TrimSpace(cfg.Version) == "" {
|
||||||
cfg.Version = "dev"
|
cfg.Version = "dev"
|
||||||
@@ -557,7 +557,7 @@ const swaggerIndexHTML = `<!doctype html>
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<title>Geo Rankly API Swagger</title>
|
<title>省心推 API Swagger</title>
|
||||||
<link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css" />
|
<link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css" />
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
|
|||||||
@@ -249,7 +249,7 @@ func RegisterRoutes(app *bootstrap.App) {
|
|||||||
|
|
||||||
if swagger.EnabledForMode(app.Config.Server.Mode) {
|
if swagger.EnabledForMode(app.Config.Server.Mode) {
|
||||||
swagger.Register(app.Engine, swagger.Config{
|
swagger.Register(app.Engine, swagger.Config{
|
||||||
Title: "Geo Rankly Admin API",
|
Title: "省心推 Admin API",
|
||||||
Version: "dev",
|
Version: "dev",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user