feat: scope articles by brand

This commit is contained in:
2026-05-20 15:37:25 +08:00
parent 5fb9d0b0dd
commit dd082e2ed1
72 changed files with 3213 additions and 432 deletions
+73 -25
View File
@@ -99,7 +99,7 @@ const enUS = {
email: 'Email',
loginIdentifier: 'Phone / Email',
password: 'Password',
rememberAccount: 'Remember account',
rememberAccount: 'Remember account and password',
forgotPassword: 'Forgot password',
},
membershipBlocked: {
@@ -137,6 +137,9 @@ const enUS = {
userFallback: 'Admin',
logout: 'Logout',
planFallback: 'Free Plan',
currentCompany: 'Company',
currentCompanyPlaceholder: 'Select company',
createCompany: 'Create company',
openNavigation: 'Open navigation',
collapseNavigation: 'Collapse navigation',
expandNavigation: 'Expand navigation',
@@ -169,6 +172,10 @@ const enUS = {
title: 'Welcome back',
description: 'Sign in with a real tenant account.',
},
brandOnboarding: {
title: 'Choose a brand',
description: 'Create the first brand and initialize its keyword library.',
},
workspace: {
title: 'Workspace',
description: 'Template shortcuts, overview data, and recent generations in one place.',
@@ -250,6 +257,47 @@ const enUS = {
description: 'The link you opened might have moved or no longer exists.',
},
},
onboardingBrand: {
eyebrow: 'Brand Onboarding',
title: 'Choose a brand or company',
subtitle: 'You have access to multiple brands or companies. Choose one to start working.',
createBrand: 'Create new brand or company',
createHint: 'After creation, keyword initialization starts automatically.',
emptyDescription:
'New users need to create a brand or company before entering the workspace.',
noWebsite: 'No website yet',
form: {
title: 'Create brand or company',
name: 'Brand or company name',
namePlaceholder: 'Example: GEO Console / Acme Furniture',
website: 'Website (optional)',
websitePlaceholder: 'https://example.com',
description: 'Description',
descriptionPlaceholder: 'Briefly describe the business, products, service area, or core value.',
},
ai: {
title: 'Generating brand or company questions',
stages: {
fetch: 'Fetching website and business context...',
generate: 'Generating candidate search terms for monitoring...',
refine: 'Deduplicating and filtering savable questions...',
},
},
questions: {
title: 'Select brand or company questions',
subtitle:
'Choose up to {limit} questions to save to the keyword library. You can add or edit more later.',
confirm: 'Save and enter workspace',
skip: 'Skip and enter',
empty: 'No usable questions were generated. You can enter the workspace and add them manually.',
selectionLimit: 'Choose up to {limit} questions.',
},
messages: {
nameRequired: 'Enter a brand or company name.',
questionRequired: 'Select at least one question, or skip and add them manually later.',
aiCharged: 'This generation consumed {count} AI point.',
},
},
notFound: {
title: 'This page got lost',
description:
@@ -723,7 +771,7 @@ const enUS = {
sourceURL: 'Article URL',
sourceTitle: 'Source title',
language: 'Article language',
brandName: 'Brand / subject',
brandName: 'Current company',
region: 'Region',
keywords: 'Optimization keywords',
preservePoints: 'Keep / emphasize',
@@ -738,17 +786,17 @@ const enUS = {
'Selected groups are retrieved in the backend and passed to generation as reference context.',
sourceURLPlaceholder: 'https://example.com/article',
sourceTitlePlaceholder: 'Optional. Citation titles are prefilled automatically.',
brandNamePlaceholder: 'Required. Type the target subject, or choose from the brand library.',
brandNamePlaceholder: 'Select the current company in the top bar first.',
regionPlaceholder: 'For example: Shanghai, East China, nationwide, North America',
keywordsPlaceholder:
'Type optimization keywords and press Enter. Brand-library options appear after choosing a subject.',
'Type optimization keywords and press Enter. Search-term options appear after selecting the current company.',
preservePointsPlaceholder: 'List facts, data, arguments, or examples that must be kept.',
avoidPointsPlaceholder: 'List claims, competitors, compliance risks, or wording to avoid.',
extraRequirementsPlaceholder:
'Add channel, structure, title style, or industry-specific instructions.',
queued: 'Rewrite task submitted and queued.',
sourceURLRequired: 'Enter the source article URL first.',
brandNameRequired: 'Enter the brand / subject first.',
brandNameRequired: 'Select the current company in the top bar first.',
submitError: 'Failed to submit rewrite task.',
backToList: 'Back to rewrites',
},
@@ -821,7 +869,7 @@ const enUS = {
drawerTitle: 'Template generation',
steps: {
basic: 'Basic info',
basicDesc: 'Language, template fields, and brand context',
basicDesc: 'Language, template fields, and company context',
structure: 'Structure',
structureDesc: 'Title and outline',
generate: 'Generate',
@@ -829,13 +877,13 @@ const enUS = {
},
sections: {
templateFields: 'Template fields',
brandContext: 'Brand context',
brandContext: 'Company context',
language: 'Language',
brandInfo: 'Brand',
brandLibrary: 'Brand library',
brandName: 'Company name',
brandInfo: 'Company',
brandLibrary: 'Company library',
brandName: 'Current company',
website: 'Official website',
brandSummary: 'AI brand summary',
brandSummary: 'AI company summary',
keywords: 'Keywords',
brandQuestions: 'Keyword library (search terms)',
primaryQuestion: 'Optimization keyword',
@@ -854,21 +902,21 @@ const enUS = {
inputParams: 'Input params',
},
hints: {
brandContext: 'Optional. Selecting a brand pulls in keywords and competitor context.',
brandContext: 'The current company pulls in keywords and competitor context.',
brandFlow:
'You can type a company name or choose one from the brand library. After you choose an optimization keyword, AI will prepare the summary, competitors, and title options.',
'Articles use the current company selected in the top bar. After you choose an optimization keyword, AI will prepare the summary, competitors, and title options.',
brandSummaryPlaceholder:
'After analysis, AI will summarize the brand angle, positioning, and content opportunities here.',
'After analysis, AI will summarize the company angle, positioning, and content opportunities here.',
keywords:
'Type keywords directly or reuse brand-library keywords. AI results will be merged into this list.',
brandQuestions:
'The optimization keyword controls the article spine; supplemental keywords broaden nearby search intent.',
supplementalQuestions:
'Choose up to 3 so the article covers related intent without drifting.',
questionEmpty: 'No questions are available for this brand yet.',
selectBrandFirst: 'Choose a library brand from the company name dropdown first.',
questionEmpty: 'No questions are available for this company yet.',
selectBrandFirst: 'Select the current company in the top bar first.',
competitors:
'Competitors stay editable. You can add rows manually; saving to the competitor library is only available after choosing a library brand.',
'Competitors stay editable. You can add rows manually; saving to the competitor library is available after selecting the current company.',
competitorEmptyEditable:
'No competitors yet. Add one manually or let AI fill the list first.',
templateFields:
@@ -883,7 +931,7 @@ const enUS = {
outlinePreview: 'The panel on the right updates from the current outline in real time.',
outlineEmpty: 'Generate the outline first to edit the article structure here.',
review: 'Check the final setup before creating the generation task.',
competitorEmpty: 'No competitors are available under this brand yet.',
competitorEmpty: 'No competitors are available under this company yet.',
customTitle: 'Use this field if you want to override the title manually.',
keyPoints: 'Add any key messages, angles, or style notes here.',
async:
@@ -904,7 +952,7 @@ const enUS = {
review: {
templateName: 'Template',
articleTitle: 'Article title',
brandContext: 'Brand context',
brandContext: 'Company context',
outline: 'Outline',
currentTemplate: 'Current template',
promptVisibility: 'Prompt visibility',
@@ -922,8 +970,8 @@ const enUS = {
actions: 'Actions',
},
placeholders: {
brandLibrary: 'Choose an existing brand from the library',
brandName: 'Enter a company name, or choose from the brand library',
brandLibrary: 'Choose the current company',
brandName: 'Current company from the top bar',
website: 'Official website, optional',
keywords: 'Type keywords and press Enter',
primaryQuestion: 'Choose one optimization keyword',
@@ -975,18 +1023,18 @@ const enUS = {
},
messages: {
requiredField: 'Please fill in {field} first.',
missingBrand: 'Enter or choose a company name first.',
missingBrand: 'Select the current company in the top bar first.',
selectBrandBeforeQuestion:
'Choose a library brand from the company name dropdown before selecting a question.',
'Select the current company in the top bar before continuing.',
missingPrimaryQuestion: 'Choose one primary brand question.',
missingTitle: 'Please choose or enter an article title.',
missingOutline: 'Please select at least one outline section.',
missingGeneratedOutline: 'Please generate and confirm the article outline first.',
customOutlineTooLong: 'Custom sections cannot exceed {count} characters.',
selectBrandBeforeCompetitors:
'Choose a library brand from the company name dropdown before managing competitors.',
'Select the current company in the top bar before managing competitors.',
selectBrandBeforeFavorite:
'Choose a library brand from the company name dropdown before saving competitors.',
'Select the current company in the top bar before saving competitors.',
missingCompetitorName: 'Enter a competitor name before saving it to the library.',
savedCompetitor: 'Competitor saved to the brand library.',
unsavedCompetitor: 'Competitor removed from the brand library.',
+71 -25
View File
@@ -101,7 +101,7 @@ const zhCN = {
email: "邮箱",
loginIdentifier: "手机号 / 邮箱",
password: "密码",
rememberAccount: "记住账号",
rememberAccount: "记住账号和密码",
forgotPassword: "忘记密码",
},
membershipBlocked: {
@@ -134,6 +134,9 @@ const zhCN = {
userFallback: "管理员",
logout: "退出",
planFallback: "免费版",
currentCompany: "当前公司",
currentCompanyPlaceholder: "选择公司",
createCompany: "创建公司",
openNavigation: "打开导航",
collapseNavigation: "收起导航",
expandNavigation: "展开导航",
@@ -165,6 +168,10 @@ const zhCN = {
title: "欢迎回来",
description: "登录省心推,开启品牌内容创作之旅",
},
brandOnboarding: {
title: "选择进入的品牌",
description: "首次进入前创建品牌,并初始化品牌关键词库。",
},
workspace: {
title: "工作台",
description: "模板入口、数据总览和最近生成都集中在这里。",
@@ -238,6 +245,45 @@ const zhCN = {
description: "你访问的链接可能已经失效或被移动。",
},
},
onboardingBrand: {
eyebrow: "Brand Onboarding",
title: "选择进入的品牌或公司",
subtitle: "你拥有多个品牌或公司的访问权限,请选择一个开始工作",
createBrand: "创建新品牌或公司",
createHint: "添加品牌或公司后会自动进入关键词初始化。",
emptyDescription: "新用户需要先创建品牌或公司,再进入工作台开始创作。",
noWebsite: "暂未填写官网",
form: {
title: "新建品牌或公司",
name: "品牌或公司名",
namePlaceholder: "例如:省心推 / 海翔家具",
website: "官网(选填)",
websitePlaceholder: "https://example.com",
description: "简介",
descriptionPlaceholder: "简单介绍公司的业务、产品、服务区域或核心卖点",
},
ai: {
title: "正在生成品牌或公司问题",
stages: {
fetch: "正在获取官网信息和业务上下文…",
generate: "正在生成适合监测的候选搜索词…",
refine: "正在去重并筛选可保存的问题…",
},
},
questions: {
title: "选择品牌或公司问题",
subtitle: "请选择最多 {limit} 个问题保存到关键词库,后续可继续添加或调整。",
confirm: "保存并进入工作台",
skip: "跳过,直接进入",
empty: "暂时没有生成可用的问题,可以先进入工作台后手动添加。",
selectionLimit: "最多选择 {limit} 个问题",
},
messages: {
nameRequired: "请输入品牌或公司名",
questionRequired: "请至少选择 1 个问题,或跳过后手动添加",
aiCharged: "本次生成已消耗 {count} 个 AI 点",
},
},
notFound: {
title: "页面走丢了",
description: "我们没有找到这个地址对应的内容,可能链接拼写有误,或者页面已经下线。",
@@ -691,7 +737,7 @@ const zhCN = {
sourceURL: "文章 URL",
sourceTitle: "来源标题",
language: "文章语言",
brandName: "品牌/主体",
brandName: "当前公司",
region: "地域",
keywords: "优化关键词",
preservePoints: "必须保留/强调",
@@ -704,15 +750,15 @@ const zhCN = {
knowledgeBaseHint: "选择后,后台会检索相关知识片段并作为生成参考,未选择则只基于来源文章和当前设置生成。",
sourceURLPlaceholder: "https://example.com/article",
sourceTitlePlaceholder: "可选,引用来源标题会自动带入",
brandNamePlaceholder: "必填,可输入目标达成词,或从品牌库选择",
brandNamePlaceholder: "请先在顶部选择当前公司",
regionPlaceholder: "例如:上海、华东、全国、北美市场",
keywordsPlaceholder: "输入优化关键词后回车,可多选;选择品牌库主体后会显示搜索词候选",
keywordsPlaceholder: "输入优化关键词后回车,可多选;选择当前公司后会显示搜索词候选",
preservePointsPlaceholder: "列出必须保留的事实、数据、观点或案例",
avoidPointsPlaceholder: "列出不能出现的说法、竞品、合规禁区或表达方式",
extraRequirementsPlaceholder: "补充行业、渠道、结构、标题风格等自由要求",
queued: "仿写任务已提交,正在排队生成",
sourceURLRequired: "请先填写来源文章 URL",
brandNameRequired: "请先填写品牌/主体",
brandNameRequired: "请先在顶部选择当前公司",
submitError: "仿写任务提交失败",
backToList: "返回仿写列表",
},
@@ -785,7 +831,7 @@ const zhCN = {
drawerTitle: "模版生成",
steps: {
basic: "基本信息",
basicDesc: "语言、模版字段与品牌上下文",
basicDesc: "语言、模版字段与公司上下文",
structure: "文章结构",
structureDesc: "标题方案与结构段落",
generate: "生成文章",
@@ -793,13 +839,13 @@ const zhCN = {
},
sections: {
templateFields: "模版字段",
brandContext: "品牌上下文",
brandContext: "公司上下文",
language: "文章语言",
brandInfo: "品牌信息",
brandLibrary: "品牌库",
brandName: "公司名称",
brandInfo: "公司信息",
brandLibrary: "公司库",
brandName: "当前公司",
website: "官网地址",
brandSummary: "AI 品牌摘要",
brandSummary: "AI 公司摘要",
keywords: "关键词",
brandQuestions: "关键词库(搜索词)",
primaryQuestion: "优化关键词",
@@ -819,15 +865,15 @@ const zhCN = {
inputParams: "输入参数",
},
hints: {
brandContext: "可选。选择后会自动带出关键词与竞品信息。",
brandFlow: "公司名称可直接输入,也可以从品牌库选择。选择优化关键词后,AI 会补充品牌摘要、竞品和标题建议。",
brandSummaryPlaceholder: "AI 分析后会在这里补充品牌简介、差异点和适合切入的内容角度。",
brandContext: "当前公司会自动带出关键词与竞品信息。",
brandFlow: "使用顶部选择的当前公司生成文章。选择优化关键词后,AI 会补充公司摘要、竞品和标题建议。",
brandSummaryPlaceholder: "AI 分析后会在这里补充公司简介、差异点和适合切入的内容角度。",
keywords: "支持直接输入,也支持复用品牌词库里的关键词。AI 分析结果会自动补充到这里。",
brandQuestions: "优化关键词决定文章主线,补充关键词只用于扩展相关搜索意图。",
supplementalQuestions: "最多选择 3 个,用于覆盖相邻搜索问题,避免文章主题发散。",
questionEmpty: "当前品牌还没有可选问题",
selectBrandFirst: "请先从公司名称下拉选择品牌库对象",
competitors: "竞品支持手动编辑、追加;只有从品牌库选择品牌后,可以收藏到品牌词库。AI 分析会尽量补齐候选竞品。",
questionEmpty: "当前公司还没有可选问题",
selectBrandFirst: "请先在顶部选择当前公司",
competitors: "竞品支持手动编辑、追加;选择当前公司后,可以收藏到公司词库。AI 分析会尽量补齐候选竞品。",
competitorEmptyEditable: "还没有竞品,先手动添加一条或点击分析让 AI 自动补齐。",
templateFields: "这里保留当前模板自己的补充字段,用来约束 AI 生成角度。",
titleSelection: "下一步前会由 AI 给出 5 个更合适的标题候选,你可以在这里挑选或改写。",
@@ -839,7 +885,7 @@ const zhCN = {
outlinePreview: "右侧会根据当前大纲实时预览最终文章框架。",
outlineEmpty: "生成大纲后,这里会显示可编辑的文章结构。",
review: "确认无误后提交,文章会进入生成队列。",
competitorEmpty: "当前品牌下暂无竞品数据",
competitorEmpty: "当前公司下暂无竞品数据",
customTitle: "如需手动改写,可在这里输入最终标题",
keyPoints: "补充希望文章强调的卖点、观点或风格要求",
async: "后端会立即创建文章和任务记录,再异步完成正文生成。提交成功后,列表会自动刷新。",
@@ -859,7 +905,7 @@ const zhCN = {
review: {
templateName: "模版名称",
articleTitle: "文章标题",
brandContext: "品牌上下文",
brandContext: "公司上下文",
outline: "结构段落",
currentTemplate: "当前模版",
promptVisibility: "Prompt 可见性",
@@ -877,8 +923,8 @@ const zhCN = {
actions: "操作",
},
placeholders: {
brandLibrary: "从品牌库选择已有品牌",
brandName: "输入公司名称,或从品牌库选择",
brandLibrary: "选择当前公司",
brandName: "顶部选择的当前公司",
website: "输入官网地址,选填",
keywords: "输入关键词后回车,可多选",
primaryQuestion: "选择一个优化关键词",
@@ -927,16 +973,16 @@ const zhCN = {
},
messages: {
requiredField: "请先填写 {field}",
missingBrand: "请先输入或选择公司名称",
selectBrandBeforeQuestion: "请先从公司名称下拉选择品牌库对象后再选择优化关键词",
missingBrand: "请先在顶部选择当前公司",
selectBrandBeforeQuestion: "请先在顶部选择当前公司后再继续",
missingPrimaryQuestion: "请选择一个优化关键词",
missingTitle: "请先选择或输入文章标题",
missingReviewIntroHook: "请先选择一个评测引言钩子",
missingOutline: "请至少选择一个文章结构段落",
missingGeneratedOutline: "请先生成并确认文章大纲",
customOutlineTooLong: "自定义结构不能超过 {count} 个字",
selectBrandBeforeCompetitors: "请先从公司名称下拉选择品牌库对象后再管理竞品",
selectBrandBeforeFavorite: "请先从公司名称下拉选择品牌库对象后再收藏竞品",
selectBrandBeforeCompetitors: "请先在顶部选择当前公司后再管理竞品",
selectBrandBeforeFavorite: "请先在顶部选择当前公司后再收藏竞品",
missingCompetitorName: "至少需要填写竞品名称后才能收藏",
savedCompetitor: "竞品已收藏到品牌词库",
unsavedCompetitor: "已取消收藏该竞品",