feat: 国际化(i18n) en/ja/ko + App 内语言切换
主体:多语言支持(简体中文源 + 英/日/韩)
- 基础设施:Localizable.xcstrings(String Catalog,sourceLanguage=zh-Hans)
+ pbxproj developmentRegion/knownRegions 注册 en/ja/ko
- 全部硬编码 Locale("zh_CN") → Locale.current;中文 dateFormat → Date.FormatStyle(跟随系统)
- UI 中文字面量统一为 String(appLoc:)(显式绑定所选语言 bundle+locale,即时切换)
Text 字面量走环境 \.locale + Bundle 重定向
- 549 个 catalog key 全部 en/ja/ko 翻译完成(0 未翻译)
- App 内语言切换:我的 → 语言(LanguageManager + 即时生效,无需重启)
- 双用预设(症状/监测指标/慢病)本地化:static→computed 避免缓存
注:本提交为 WIP,一并打包了并行进行的功能模块
(HealthExport 健康导出、Security/Face ID 锁、DiaryAssist 日记 AI 辅助)
及 App 图标、CLAUDE.md、docs/scripts。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
68
scripts/fetch-qwen3vl.sh
Executable file
68
scripts/fetch-qwen3vl.sh
Executable file
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env bash
|
||||
# 下载 Qwen3-VL-4B-Instruct-4bit(MLX 4bit)全量文件到本地镜像目录,并逐个校验字节数。
|
||||
# 字节数权威来源:康康/AI/ModelManifest.swift(HF API blobs=true,2026-05 核对)。
|
||||
# 用法: bash scripts/fetch-qwen3vl.sh
|
||||
set -uo pipefail
|
||||
|
||||
REPO="mlx-community/Qwen3-VL-4B-Instruct-4bit"
|
||||
BASE="https://huggingface.co/${REPO}/resolve/main"
|
||||
# 目标 = 康康仓库内的 Models/(已被 .gitignore 忽略,App 旁路导入也认这个目录名)。
|
||||
# 可用环境变量 KK_MODELS_DIR 覆盖根目录(如指向另一块盘)。
|
||||
ROOT="${KK_MODELS_DIR:-/Users/xuhuayong/apps/康康/Models}"
|
||||
DEST="$ROOT/Qwen3-VL-4B-Instruct-4bit"
|
||||
mkdir -p "$DEST"
|
||||
|
||||
# 文件名:期望字节数(与 ModelManifest.swift 的 .vl 清单一一对应)
|
||||
FILES=(
|
||||
"config.json:7137"
|
||||
"model.safetensors:3093767283"
|
||||
"model.safetensors.index.json:64742"
|
||||
"tokenizer.json:11422654"
|
||||
"tokenizer_config.json:5445"
|
||||
"vocab.json:2776833"
|
||||
"merges.txt:1671853"
|
||||
"special_tokens_map.json:613"
|
||||
"added_tokens.json:707"
|
||||
"generation_config.json:269"
|
||||
"chat_template.json:5502"
|
||||
"chat_template.jinja:5292"
|
||||
"preprocessor_config.json:782"
|
||||
"video_preprocessor_config.json:817"
|
||||
)
|
||||
|
||||
fsize() { stat -f%z "$1" 2>/dev/null || echo 0; }
|
||||
|
||||
fail=0
|
||||
for entry in "${FILES[@]}"; do
|
||||
name="${entry%%:*}"; want="${entry##*:}"; out="$DEST/$name"
|
||||
if [[ -f "$out" && "$(fsize "$out")" == "$want" ]]; then
|
||||
echo "SKIP $name (已完整 $want)"; continue
|
||||
fi
|
||||
echo "GET $name (期望 $want 字节)"
|
||||
curl -fL -C - --retry 5 --retry-delay 3 --connect-timeout 30 \
|
||||
-o "$out" "$BASE/$name" || { echo " !! 下载失败 $name"; fail=1; continue; }
|
||||
have="$(fsize "$out")"
|
||||
if [[ "$have" != "$want" ]]; then
|
||||
echo " !! 字节不符 $name: 实得 $have / 期望 $want"; fail=1
|
||||
else
|
||||
echo " OK $name $have"
|
||||
fi
|
||||
done
|
||||
|
||||
# 大权重额外做 SHA256 校验(HF LFS oid,密码学级,字节数相同也能查出脏数据)。
|
||||
WEIGHT_SHA="90eeb02604181dbcccd0a30a1f550a4a8928ca7dcbee4aee1449239306cfdfca"
|
||||
if [[ -f "$DEST/model.safetensors" ]]; then
|
||||
echo "校验 model.safetensors SHA256(约需 10 余秒)..."
|
||||
got="$(shasum -a 256 "$DEST/model.safetensors" | awk '{print $1}')"
|
||||
if [[ "$got" == "$WEIGHT_SHA" ]]; then
|
||||
echo " ✓ SHA256 匹配"
|
||||
else
|
||||
echo " !! SHA256 不符: 实得 $got / 期望 $WEIGHT_SHA"; fail=1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "================================================"
|
||||
total=$(du -sh "$DEST" 2>/dev/null | cut -f1)
|
||||
echo "目录: $DEST (合计 $total)"
|
||||
if [[ "$fail" == "0" ]]; then echo "✅ 全部 14 个文件下载并校验通过(权重含 SHA256)"; else echo "❌ 有文件失败,重跑本脚本可断点续传"; fi
|
||||
exit "$fail"
|
||||
53
scripts/upload-qwen3vl.sh
Normal file
53
scripts/upload-qwen3vl.sh
Normal file
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env bash
|
||||
# 把本地 Models/Qwen3-VL-4B-Instruct-4bit/ 的 14 个文件上传到模型分发服务器,
|
||||
# 使 App 的「模型管理 · 下载」能拉到新 VL 模型(否则用户点下载会 404)。
|
||||
#
|
||||
# 服务器:Caddy(file_server browse),web 根 = /srv/models,SSH = root@101.132.124.52。
|
||||
# App 下载 URL 形如:https://file.myv0.com/Qwen3-VL-4B-Instruct-4bit/<file>
|
||||
# → openresty(终止 HTTPS)回源到 Caddy :80(root /srv/models)。
|
||||
# → 所以远端目标目录 = /srv/models/Qwen3-VL-4B-Instruct-4bit/。
|
||||
#
|
||||
# 认证:已用 ssh-copy-id 装好本机公钥,走免密 key;脚本内不含任何密码。
|
||||
# 用法: bash scripts/upload-qwen3vl.sh
|
||||
set -euo pipefail
|
||||
|
||||
LOCAL_DIR="/Users/xuhuayong/apps/康康/Models/Qwen3-VL-4B-Instruct-4bit"
|
||||
SSH_HOST="root@101.132.124.52"
|
||||
REMOTE_ROOT="/srv/models"
|
||||
REMOTE_SUBDIR="Qwen3-VL-4B-Instruct-4bit"
|
||||
REMOTE_DIR="$REMOTE_ROOT/$REMOTE_SUBDIR"
|
||||
|
||||
# 上传前本地完整性自检(逐字节,14 文件全 SKIP 才算齐)。
|
||||
bash "$(dirname "$0")/fetch-qwen3vl.sh" >/dev/null || { echo "本地文件不完整,先跑 fetch-qwen3vl.sh 修复再上传"; exit 1; }
|
||||
echo "本地 14 文件校验通过,开始上传 → $SSH_HOST:$REMOTE_DIR/"
|
||||
|
||||
ssh -o ConnectTimeout=20 "$SSH_HOST" "mkdir -p '$REMOTE_DIR'"
|
||||
|
||||
# rsync 断点续传(-P=--partial --progress),--inplace 适合大文件。
|
||||
# 注意:macOS 自带 rsync 2.6.9 不支持 --info=progress2,用 -P 即可。
|
||||
rsync -avP --inplace \
|
||||
-e "ssh -o ConnectTimeout=20" \
|
||||
"$LOCAL_DIR/" "$SSH_HOST:$REMOTE_DIR/"
|
||||
|
||||
echo "✅ rsync 上传完成,开始远端校验..."
|
||||
|
||||
# 远端逐文件大小核对(与本地 ModelManifest 的 14 文件一致)。
|
||||
ssh "$SSH_HOST" "cd '$REMOTE_DIR' && ls -la && echo '--- 总大小 ---' && du -sh ."
|
||||
|
||||
cat <<'TIP'
|
||||
──────────────────────────────────────────────
|
||||
上传完成。建议再从公网验证一次(应全部 HTTP 200,content-length 与本地一致):
|
||||
|
||||
for f in config.json model.safetensors model.safetensors.index.json \
|
||||
tokenizer.json tokenizer_config.json vocab.json merges.txt \
|
||||
special_tokens_map.json added_tokens.json generation_config.json \
|
||||
chat_template.json chat_template.jinja preprocessor_config.json \
|
||||
video_preprocessor_config.json; do
|
||||
curl -sI "https://file.myv0.com/Qwen3-VL-4B-Instruct-4bit/$f" \
|
||||
| awk -v F="$f" '/^HTTP/{c=$2} tolower($1)=="content-length:"{s=$2} END{printf "%-32s %s %s\n",F,c,s}'
|
||||
done
|
||||
|
||||
旧模型 Qwen2.5-VL-3B 仍在服务器上;确认新版可用后再删旧目录:
|
||||
ssh root@101.132.124.52 'rm -rf /srv/models/Qwen2.5-VL-3B-Instruct-4bit'
|
||||
──────────────────────────────────────────────
|
||||
TIP
|
||||
Reference in New Issue
Block a user