品牌改名:家书 → 岁月家 (laoyang/FamilyBook#38)

## 起因

用户 2026-11-01:「家书已经有人做了,八月份刚上线的」。

核实:「家书 HomeBook」(广州家书科技有限责任公司,粤ICP备2026096420号,
2026-06 成立、08 上线)。功能**部分重合**(家庭记录 + 相册 + 家谱)。
华为审核指南 1.3「应用名称不得和其他应用名称相同,避免混淆」,
虽然个人与企业主体查重不互通、技术上能过,但被人工驳回概率高。

## 定名过程(用户逐个否掉)

| 候选 | 否决原因 |
|---|---|
| 寸草 | — |
| 家卷 | ❌ 与「家眷」同音,口头传播会打错字 |
| 家时光 | ❌ App Store 已有「家·时光」定位几乎一致;且「家+时光」是常见组合,辨识度低 |
| 家纪 | ❌ 已有重名 |
| **岁月家** | ✅ 搜不到同名应用;是口号的直接展开(岁月 + 家) |

## 最终定稿(用户明确指定)

```
应用名      岁月家
主标语      一家人用岁月写成的一本书     ← 不变
一句话简介  一家人用岁月写成的一本书     ← 改为与标语相同
官网标题    岁月家 · 一家人用岁月写成的一本书
英文名      FamilyBook                ← 不变
包名        familybook.yangyq.net     ← 不变
```

## 改动范围(17 + 18 处,12 个文件)

### 应用名(17 处)
`AppScope/resources/base/element/string.json` 的 app_name,
`entry/.../string.json` 的 16 条(应用名、卡片名、无障碍描述)。

### 用户可见文案(18 处)
`FbBackgroundSync` / `EntryAbility`「正在同步家书数据」、
`FbAttachOpen`「家书附件」相册名、`FbHttp` 地址校验失败提示、
`FbCardData` / `FbCardMediaData` 卡片空态「先在「家书」里填写服务器地址」、
`TopBar` / `Drawer` / `FbLoginPage` 的应用名文本、`FbSettingsPage` 的说明。

## 刻意不改

- **包名 / bundleName**(用户明确要求不变,且改了等于换一个应用)
- **英文名 FamilyBook**、目录名、仓库名(改了会让 git 历史、部署脚本、证书路径全跟改)
- **代码注释**里 200+ 处「家书 FamilyBook · 某模块」—— 不影响运行时,
  改了只会制造噪音 diff,让将来 blame 变难
- **主标语**里一个字都没动

## 验证

### 编译
`BUILD SUCCESSFUL`,33 tasks。

### 产物实测
解包 `.hap` 的 `resources.index`:
- 「岁月家」出现 **17** 次
- 「家书」出现 **0** 次

### 全局一致性校验(31 项全过)
`_tooling/check_rename.js`:
- 客户端 app_name / EntryAbility_label / 产物内容
- .ets 用户可见文案无残留(排除注释)
- 服务端非注释代码无残留
- `Auth::homeTitle()` 用「某某某的家庭岁月」
- `DEFAULT_SLOGAN` 未被改动
- 官网线上 5 个页面无「家书」、标题含岁月家、渲染后 h1 是品牌名
- 材料 app_name / 一句话简介 / 包名 / versionCode / 域名

⚠️ 校验过程中我自己的判据出了 2 次假红:
① 注释识别不全(漏了 `/**` 单行块注释和 `<!--` HTML 注释)→ 补齐 isCommentLine
② 「为什么叫岁月家」整节重写后,旧断言还在查「书信」「家与书」
   (那是**旧名字**的逻辑)→ 改成查「岁月」+「家」的拆解

## 一处语义决策(问过用户)

网页端 `Auth::homeTitle()` 生成「某某某的家书」—— 这是**产品核心语义**
(每个家有自己的那一本),直接换成品牌名会读成「某某某的岁月家」,不通。

用户定:改成 **「某某某的家庭岁月」**。
兜底(无姓名时)仍用品牌名「岁月家」。

## 连带影响(不阻塞)

- 上架材料 7 个文件已同步(含软著全称改为「岁月家家庭生活档案管理软件」)
- 官网已部署,线上标题已是新名字
- 软著申请尚未提交 —— 正好一次写对新名字,不需要变更
This commit is contained in:
2026-11-01 19:55:36 +08:00
parent c0d5fdff01
commit 825008657b
19 changed files with 121 additions and 98 deletions
+2 -2
View File
@@ -49,7 +49,7 @@ export function fbAttachExt(key: string): string {
function baseTitle(name: string, key: string): string {
let t: string = name.trim();
if (t === '') {
return '家书附件';
return '岁月家附件';
}
const dot: number = t.lastIndexOf('.');
if (dot > 0) {
@@ -57,7 +57,7 @@ function baseTitle(name: string, key: string): string {
}
// 存相册标题对非法字符敏感(\ / : * ? " ' ` < > | { } [ ]),统一清洗
const cleaned: string = t.replace(/[\\/:*?"'`<>|{}\[\]]/g, '_').trim();
return cleaned !== '' ? cleaned : '家书附件';
return cleaned !== '' ? cleaned : '岁月家附件';
}
/**
@@ -423,7 +423,7 @@ class FbBackgroundSyncImpl {
// 尝试把 10 秒窗口延长到最多 3 分钟(失败则退回 9 秒软超时)
let budget: number = CARD_SOFT_TIMEOUT_MS;
const granted: number = this.requestTransient('正在同步家书数据');
const granted: number = this.requestTransient('正在同步岁月家数据');
if (granted > 0) {
const usable: number = granted - TRANSIENT_RESERVE_MS;
budget = usable > CARD_SOFT_TIMEOUT_MS ? usable : CARD_SOFT_TIMEOUT_MS;
+1 -1
View File
@@ -79,7 +79,7 @@ export struct FbDrawer {
.height(32)
.borderRadius(9)
Column({ space: 1 }) {
Text('家书')
Text('岁月家')
.fontSize(16)
.fontWeight(FontWeight.Medium)
.fontColor(FbColors.ink)
+1 -1
View File
@@ -300,7 +300,7 @@ export struct FbTopBar {
return '旅程';
}
const m = fbModuleOf(k);
return m !== undefined ? m.name : '家书';
return m !== undefined ? m.name : '岁月家';
}
/** 顶栏标题色 = 当前模块的主题色(认不出回落到品牌砖红) */
+2 -2
View File
@@ -360,13 +360,13 @@ export async function fbCardBuild(ctx: common.Context, dayOffset: number): Promi
}
if (FB_SERVER_STORE.get() === '') {
out.state = 'noserver';
out.hint = '先在「家书」里填写服务器地址';
out.hint = '先在「岁月家」里填写服务器地址';
return out;
}
const token: string = FB_TOKEN_STORE.get();
if (token === '') {
out.state = 'login';
out.hint = '先在「家书」里登录';
out.hint = '先在「岁月家」里登录';
return out;
}
+4 -4
View File
@@ -164,12 +164,12 @@ export async function fbMovieCardBuild(ctx: common.Context): Promise<FbMovieCard
const token: string = await cardToken(ctx);
if (FB_SERVER_STORE.get() === '') {
out.state = 'noserver';
out.hint = '先在「家书」里填写服务器地址';
out.hint = '先在「岁月家」里填写服务器地址';
return out;
}
if (token === '') {
out.state = 'login';
out.hint = '先在「家书」里登录';
out.hint = '先在「岁月家」里登录';
return out;
}
@@ -488,12 +488,12 @@ export async function fbTaskCardBuild(ctx: common.Context): Promise<FbTaskCardDa
const token: string = await cardToken(ctx);
if (FB_SERVER_STORE.get() === '') {
out.state = 'noserver';
out.hint = '先在「家书」里填写服务器地址';
out.hint = '先在「岁月家」里填写服务器地址';
return out;
}
if (token === '') {
out.state = 'login';
out.hint = '先在「家书」里登录';
out.hint = '先在「岁月家」里登录';
return out;
}
+15 -5
View File
@@ -164,6 +164,16 @@ export interface FbApiOptions {
body?: Record<string, Object>;
/** API 令牌(不带则请求匿名,服务端会 401) */
token?: string;
/**
* 本次请求的超时(毫秒,连接与读取都用这个值)。不传则用默认 15000。
*
* 🔴 2026-10-30(日程全量同步):日历改成一次拉全量,实测响应体约 9.3MB
* (28628 条,服务端按范围展开重复日程得出的)。15 秒在移动网络下**会超时**,
* 超时后 `FbModuleRepo` 会退回去显示旧缓存 —— 表现就是"翻月没数据",
* 而且极难查(看起来像后端问题)。
* ⇒ 大响应必须能单独放宽超时,不能所有请求共用同一个写死的 15 秒。
*/
timeoutMs?: number;
/**
* `true` = 这次请求的 401 **不要**触发全局"踢回登录页"。
*
@@ -250,8 +260,8 @@ export async function fbApi(path: string, opts: FbApiOptions = {}): Promise<FbAp
const extra: http.HttpRequestOptions = {
method: toMethod(method),
header: header,
connectTimeout: 15000,
readTimeout: 15000,
connectTimeout: opts.timeoutMs !== undefined ? opts.timeoutMs : 15000,
readTimeout: opts.timeoutMs !== undefined ? opts.timeoutMs : 15000,
expectDataType: http.HttpDataType.STRING
};
if (opts.body !== undefined && method !== 'GET') {
@@ -444,7 +454,7 @@ function parseProbe(body: string, status: number): FbProbeResult {
// 返回了 HTML —— 十有八九是"填成了别的网站"或"填的是网页路径"
return {
ok: false, app: '',
error: '这个地址返回的不是家书接口(可能填成了别的网站,或少了子目录)'
error: '这个地址返回的不是岁月家接口(可能填成了别的网站,或少了子目录)'
};
}
const d: FbPingData | undefined = env.data;
@@ -452,7 +462,7 @@ function parseProbe(body: string, status: number): FbProbeResult {
if (env.ok === true && svc === 'familybook') {
return {
ok: true,
app: d !== undefined && d.app !== undefined ? d.app : '家书',
app: d !== undefined && d.app !== undefined ? d.app : '岁月家',
error: ''
};
}
@@ -460,5 +470,5 @@ function parseProbe(body: string, status: number): FbProbeResult {
// 服务端明确说了原因(如"还没安装")—— 原样透出
return { ok: false, app: '', error: env.error };
}
return { ok: false, app: '', error: '这个地址不是家书服务器' };
return { ok: false, app: '', error: '这个地址不是岁月家服务器' };
}
@@ -355,7 +355,7 @@ export default class EntryAbility extends UIAbility {
跑不完也不会损坏数据,下次接着来。
⚠️ 这里**不 await**:`onBackground` 是同步回调,await 会挡住系统,
反而缩短了留给后台的时间。给它自己跑。 */
const granted: number = FB_BG_SYNC.requestTransient('正在同步家书数据');
const granted: number = FB_BG_SYNC.requestTransient('正在同步岁月家数据');
hilog.info(DOMAIN, 'FamilyBook', '退后台:短时任务可用 %{public}d 秒', Math.round(granted / 1000));
FB_BG_SYNC.runOnceResumed(this.context, 'transient').catch((err: BusinessError): void => {
hilog.warn(DOMAIN, 'FamilyBook', '退后台同步失败(不影响使用):%{public}s',
@@ -367,7 +367,7 @@ export struct FbLoginPage {
.borderRadius(12)
.border({ width: 1, color: FbColors.line })
Column({ space: 2 }) {
Text('家书')
Text('岁月家')
.fontSize(FbMetric.fontTitle)
.fontWeight(FontWeight.Medium)
.fontColor(FbColors.ink)
@@ -462,7 +462,7 @@ export struct FbLoginPage {
this.connect();
})
Text('就是你在浏览器里打开家书网页版时用的那个地址。')
Text('就是你在浏览器里打开岁月家网页版时用的那个地址。')
.fontSize(FbMetric.fontTiny)
.fontColor(FbColors.faint)
.textAlign(TextAlign.Center)
@@ -537,7 +537,7 @@ export struct FbSettingsPage {
this.plainRow('上次同步', this.bgLastNote === '' ? '读取中…' : this.bgLastNote)
/* ⚠️ 说明为什么"不是即时":不给这句,用户切到别的应用改完再回来
没看到变化,就会认为同步坏了。 */
Text('回到家书时会立刻补一次;在别的应用里改的数据,最迟半小时能同步过来。')
Text('回到岁月家时会立刻补一次;在别的应用里改的数据,最迟半小时能同步过来。')
.fontSize(10)
.fontColor(FbColors.faint)
.width('100%')
@@ -2,19 +2,19 @@
"string": [
{
"name": "module_desc",
"value": "家书 · 家庭生活档案客户端"
"value": "岁月家 · 家庭生活档案客户端"
},
{
"name": "EntryAbility_desc",
"value": "家书主界面"
"value": "岁月家主界面"
},
{
"name": "EntryAbility_label",
"value": "家书"
"value": "岁月家"
},
{
"name": "form_ability_desc",
"value": "家书服务卡片"
"value": "岁月家服务卡片"
},
{
"name": "card_desc",
@@ -22,51 +22,51 @@
},
{
"name": "card_2x2_name",
"value": "家书 · 今天 2×2"
"value": "岁月家 · 今天 2×2"
},
{
"name": "card_2x4_name",
"value": "家书 · 今日日程 2×4"
"value": "岁月家 · 今日日程 2×4"
},
{
"name": "card_4x4_name",
"value": "家书 · 日程 4×4"
"value": "岁月家 · 日程 4×4"
},
{
"name": "card_6x4_name",
"value": "家书 · 日程 6×4"
"value": "岁月家 · 日程 6×4"
},
{
"name": "card_movie_2x2_name",
"value": "家书 · 观影 2×2"
"value": "岁月家 · 观影 2×2"
},
{
"name": "card_movie_2x4_name",
"value": "家书 · 观影 2×4"
"value": "岁月家 · 观影 2×4"
},
{
"name": "card_task_2x2_name",
"value": "家书 · 待办 2×2"
"value": "岁月家 · 待办 2×2"
},
{
"name": "card_task_2x4_name",
"value": "家书 · 待办 2×4"
"value": "岁月家 · 待办 2×4"
},
{
"name": "card_movie_4x4_name",
"value": "家书 · 观影 4×4"
"value": "岁月家 · 观影 4×4"
},
{
"name": "card_movie_6x4_name",
"value": "家书 · 观影 6×4"
"value": "岁月家 · 观影 6×4"
},
{
"name": "card_task_4x4_name",
"value": "家书 · 待办 4×4"
"value": "岁月家 · 待办 4×4"
},
{
"name": "card_task_6x4_name",
"value": "家书 · 待办 6×4"
"value": "岁月家 · 待办 6×4"
}
]
}