Files
eryao/.trellis/tasks/archive/2026-04/04-28-feat-locale-timezone-bootstrap/prd.md
T

3.6 KiB

PRD: App启动时语言和时区自动设置

背景

当前App启动时语言硬编码为 zh,不读取iOS系统语言;时区完全不处理。需要实现三种场景的自动设置逻辑。

需求

场景1:新用户首次打开App → 注册时写入后端

  1. App无本地存储信息(首次打开)
  2. 读取iOS系统语言和时区
  3. 自动设置App界面语言和时区
  4. 用户注册时,将语言和时区作为请求参数传给后端
  5. 后端创建用户Profile时使用传入值

场景2:已有用户在新设备首次登录 → 从服务器同步

  1. App无本地存储信息(新设备首次打开)
  2. 读取iOS系统语言和时区作为临时值
  3. 自动设置App界面语言和时区(临时)
  4. 用户登录成功后,从服务器拉取Profile中的语言和时区
  5. 用服务器值更新本地存储和App设置

场景3:有本地存储 → 使用本地值

  1. App有本地存储的语言和时区信息
  2. 直接使用本地值,不读取系统语言和时区
  3. 登录后仍从服务器同步(确保一致性)

技术方案

前端

  1. 读取系统语言

    • 使用 PlatformDispatcher.instance.locale 获取系统首选语言
    • 通过 resolveSystemLocale() 映射到 App 支持的 Locale
  2. 读取系统时区

    • 使用 flutter_timezone 包的 getLocalTimezone() 获取 IANA 时区 ID
    • 直接使用返回值,后端会验证有效性
  3. 本地存储扩展

    • SessionStore 新增 saveTimezone() / getTimezone()
    • 现有 saveLocaleTag() / getLocaleTag() 已支持语言
  4. 启动流程修改 (app.dart:_bootstrap)

    if (本地有locale) {
      使用本地locale
    } else {
      读取系统locale → 保存到本地
    }
    
    if (本地有时区) {
      使用本地时区
    } else {
      读取系统时区 → 保存到本地
    }
    
  5. 注册请求扩展

    • AuthApi.createEmailSession 新增 languagetimezone 参数
    • EmailSessionCreateRequest schema 扩展
  6. 登录后同步

    • 现有 _refreshProfile() 已同步语言
    • 扩展同步时区

后端

  1. Schema扩展 (v1/auth/schemas.py)

    class EmailSessionCreateRequest(BaseModel):
        email: str
        token: str
        language: str | None = None  # BCP-47 tag
        timezone: str | None = None  # IANA timezone
    
  2. Service逻辑 (v1/auth/service.py)

    • create_email_session 接收语言/时区
    • 创建/更新Profile时使用传入值(如果提供)
  3. Profile默认值保持不变

    • language: str = "zh-CN"
    • timezone: str = "Asia/Shanghai"

验收标准

  1. 新设备首次打开App,界面语言与iOS系统语言一致
  2. 新用户注册后,后端Profile的语言/时区与App一致
  3. 已有用户登录后,App语言/时区与服务器Profile一致
  4. 有本地存储时,App启动不请求系统语言/时区
  5. 支持的语言映射:
    • iOS en / en-US / en-GB → Flutter Locale('en') → 存储 en-US
    • iOS zh / zh-CN / zh-SG / zh-Hans-* → Flutter Locale('zh') → 存储 zh-CN
    • iOS zh-Hant / zh-TW / zh-HK / zh-MO → Flutter Locale(zh, Hant) → 存储 zh-Hant
  6. 时区为有效IANA ID (如 Asia/Shanghai, America/New_York)

风险

  1. iOS系统语言可能不在支持列表中 → 回退到 zh-CN
  2. flutter_timezone 包可能返回无效时区 → 后端验证 + 前端回退默认值
  3. 用户修改系统语言/时区后,App不会自动更新(符合场景3设计)
  4. 新用户注册时网络请求失败 → Profile 使用后端默认值,用户可在设置中修改