|
|
@@ -1,12 +1,13 @@
|
|
|
#!/usr/bin/env python3
|
|
|
# -*- coding: utf-8 -*-
|
|
|
-"""Generate LoopFollow/Resources/Localizable.xcstrings from a zh-Hans dict.
|
|
|
+"""Update LoopFollow/Resources/Localizable.xcstrings from a zh-Hans dict.
|
|
|
Keys must match the exact source literal so SwiftUI auto-localizes.
|
|
|
-Strings NOT in the dict are simply omitted -> app falls back to English.
|
|
|
+Existing catalog entries are preserved so Xcode-extracted source strings,
|
|
|
+plural variants, comments, and other languages are not discarded.
|
|
|
"""
|
|
|
import json, collections, os
|
|
|
|
|
|
-APP = "/Users/luming/Desktop/loopfollow/loopfollow/LoopFollow"
|
|
|
+APP = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "LoopFollow")
|
|
|
OUT = os.path.join(APP, "Resources", "Localizable.xcstrings")
|
|
|
|
|
|
# English source -> 简体中文. Terminology aligned with Trio-Diy glossary.
|
|
|
@@ -539,7 +540,7 @@ add([
|
|
|
("Pump Battery Alert", "泵电量告警"),
|
|
|
("Low Battery", "电量过低"),
|
|
|
("Battery Drop", "电量骤降"),
|
|
|
- ("Rec. Bolus", "推荐大剂量"),
|
|
|
+ ("Rec. Bolus", "推荐剂量"),
|
|
|
("Override Started", "临时配置已开始"),
|
|
|
("Override Ended", "临时配置已结束"),
|
|
|
("Temp Target Started", "临时目标已开始"),
|
|
|
@@ -987,24 +988,178 @@ add([
|
|
|
("Unknown", "未知"),
|
|
|
])
|
|
|
|
|
|
+# ==== 三轮:首页 min ago 与缩写行名(用户点名) ====
|
|
|
+add([
|
|
|
+ (" min ago", " 分钟前"),
|
|
|
+ ("?? min ago", "?? 分钟前"),
|
|
|
+ ("Refreshing", "刷新中"),
|
|
|
+ (" min", " 分钟"),
|
|
|
+ ("IOB", "活性胰岛素"),
|
|
|
+ ("COB", "活性碳水"),
|
|
|
+ ("SAGE", "传感器"),
|
|
|
+ ("CAGE", "管路"),
|
|
|
+ ("IAGE", "胰岛素"),
|
|
|
+ ("TDD", "每日总量"),
|
|
|
+ ("ISF", "敏感系数"),
|
|
|
+ ("CR", "碳水系数"),
|
|
|
+])
|
|
|
+
|
|
|
+# ==== 三轮:版本提醒 / 身份验证 / 构建信息 ====
|
|
|
+add([
|
|
|
+ ("Update Available", "有可用更新"),
|
|
|
+ ("The current version has a critical issue and should be updated as soon as possible.", "当前版本存在严重问题,请尽快更新。"),
|
|
|
+ ("A new version is available: %@. It is recommended to update.", "新版本已发布:%@。建议尽快更新。"),
|
|
|
+ ("Enter Passcode", "输入设备密码"),
|
|
|
+ ("Device authentication is not available. ", "设备身份验证不可用。"),
|
|
|
+ ("Please enable Face ID, Touch ID, or set up a device passcode in Settings.", "请在「设置」中启用面容 ID、触控 ID,或设置设备密码。"),
|
|
|
+ ("Face ID is not available. Please set up a device passcode in Settings.", "面容 ID 不可用。请在「设置」中设置设备密码。"),
|
|
|
+ ("Touch ID is not available. Please set up a device passcode in Settings.", "触控 ID 不可用。请在「设置」中设置设备密码。"),
|
|
|
+ ("TestFlight Expires", "TestFlight 到期"),
|
|
|
+ ("App Expires", "应用到期"),
|
|
|
+ ("Search Log", "搜索日志"),
|
|
|
+ ("No Logs Available", "暂无日志"),
|
|
|
+ ("There are no logs to share.", "没有可分享的日志。"),
|
|
|
+])
|
|
|
+
|
|
|
+# ==== 三轮:时间单位 / 闹钟分组 / 默认闹钟名 ====
|
|
|
+add([
|
|
|
+ ("none", "无"),
|
|
|
+ ("Until", "直到"),
|
|
|
+ ("Insulin / Food", "胰岛素 / 食物"),
|
|
|
+ ("Device / System", "设备 / 系统"),
|
|
|
+ ("Override / Target", "临时配置 / 目标"),
|
|
|
+ ("Urgent Low", "紧急低血糖"),
|
|
|
+ ("Urgent High", "紧急高血糖"),
|
|
|
+ ("Fast Drop", "快速下降"),
|
|
|
+ ("Fast Rise", "快速上升"),
|
|
|
+ ("Missed Reading", "读数缺失"),
|
|
|
+ ("Sensor Change Reminder", "传感器更换提醒"),
|
|
|
+ ("Pump / Cannula Change", "泵/管路更换"),
|
|
|
+ ("Temporary BG Limit", "临时血糖限值"),
|
|
|
+ ("Pump Reservoir", "泵储药量"),
|
|
|
+ ("High IOB", "高活性胰岛素"),
|
|
|
+ ("High COB", "高活性碳水"),
|
|
|
+ ("Snoozed until %@", "已暂停至 %@"),
|
|
|
+ ("%lld sec", "%lld 秒"),
|
|
|
+ ("up to %lld sec", "最多 %lld 秒"),
|
|
|
+ ("%lld seconds", "%lld 秒"),
|
|
|
+ ("“Default Snooze” controls the default value for how long the alert stays quiet after you press Snooze. \"A snooze duration of 0 means the alarm is acknowledged (silenced), and will alert again next time the condition applies, without time limitation. \" Toggle “Snoozed” to mute this alarm right now.",
|
|
|
+ "「默认暂停时长」控制按下暂停后提醒静默多久的默认值。暂停时长为 0 表示仅确认(静音)该提醒,下次条件满足时仍会再次提醒,不受时间限制。打开「已暂停」可立即静音此闹钟。"),
|
|
|
+])
|
|
|
+
|
|
|
+# ==== 三轮:闹钟编辑器介绍段 ====
|
|
|
+add([
|
|
|
+ ("This warns you if your phone’s battery drops quickly, based on the percentage and time you set.", "当手机电量按你设定的幅度和时间快速下降时提醒你。"),
|
|
|
+ ("Alerts when Carbs-on-Board is at or above the amount you set below.", "当活性碳水达到或超过下方设定值时提醒。"),
|
|
|
+ ("Alerts when glucose readings rise rapidly. For example, three straight readings each climbing by at least the amount you set. Optionally limit alerts to only fire above a certain BG.",
|
|
|
+ "当血糖读数快速上升时提醒。例如连续三次读数、每次上升都不低于你设定的幅度。还可限制仅在血糖高于某个值时才提醒。"),
|
|
|
+ ("Alerts when glucose stays above the limit you set below. Use Persistent if you want to ignore brief spikes.",
|
|
|
+ "当血糖持续高于下方设定的限值时提醒。若想忽略短暂冲高,请使用「持续时间」。"),
|
|
|
+ ("Sends a reminder before the looping-app build you’re following reaches its TestFlight or Xcode expiry date. Works with Trio 0.4 and later.",
|
|
|
+ "在你跟随的闭环应用构建到达 TestFlight 或 Xcode 到期日前发送提醒。适用于 Trio 0.4 及以上版本。"),
|
|
|
+ ("Alerts when glucose readings drop rapidly. For example, three straight readings each falling by at least the amount you set. Optionally limit alerts to only fire below a certain BG level.",
|
|
|
+ "当血糖读数快速下降时提醒。例如连续三次读数、每次下降都不低于你设定的幅度。还可限制仅在血糖低于某个值时才提醒。"),
|
|
|
+ ("Alerts when a future-dated carb entry's scheduled time arrives — a reminder to start eating. Use the max lookahead to ignore fat/protein entries that are typically scheduled further ahead.",
|
|
|
+ "当未来时间的碳水记录到点时提醒——提示该吃东西了。用最长预看时间可忽略通常安排得更靠后的脂肪/蛋白质条目。"),
|
|
|
+ ("Alerts when carbs are logged but no bolus is delivered within the delay below. Allows small-carb / treatment exclusions and pre-bolus detection.",
|
|
|
+ "记录碳水后,若在下方延迟时间内未输注大剂量则提醒。支持排除少量碳水,并可识别餐前大剂量。"),
|
|
|
+ ("This warns you when the phone's battery gets low, based on the percentage you choose.", "当手机电量低于你选择的百分比时提醒你。"),
|
|
|
+ ("Alerts when insulin-on-board is high, or when several boluses in quick succession exceed the limits you set.",
|
|
|
+ "当活性胰岛素偏高,或短时间内多次大剂量超过你设定的限值时提醒。"),
|
|
|
+ ("Alerts when the pump / cannula is within the time window you choose below (relative to the 3-day change limit).",
|
|
|
+ "当泵/管路进入你在下方选择的时间窗口(相对 3 天更换期限)时提醒。"),
|
|
|
+ ("Alerts when the recommended bolus equals or exceeds the threshold you set below.", "当推荐大剂量达到或超过下方设定的阈值时提醒。"),
|
|
|
+ ("Alerts when an override begins.", "当临时配置开始时提醒。"),
|
|
|
+ ("Alerts when an override ends.", "当临时配置结束时提醒。"),
|
|
|
+ ("Alerts when a temp target starts.", "当临时目标开始时提醒。"),
|
|
|
+ ("Alerts when a temp target ends.", "当临时目标结束时提醒。"),
|
|
|
+ ("This alert fires once when glucose crosses either of the limits you set below, and then disables itself.",
|
|
|
+ "当血糖越过下方任一限值时提醒一次,然后自动停用。"),
|
|
|
+ ("This warns you when the insulin pump is running low on insulin.", "当胰岛素泵的药量不足时提醒你。"),
|
|
|
+ ("Alerts when no successful loop has occurred for the time you set below.", "当超过下方设定时间仍未成功闭环时提醒。"),
|
|
|
+ ("This warns you if the glucose is too low now or might be soon, based on the forecast.",
|
|
|
+ "当血糖现在过低、或根据预测即将过低时提醒你。"),
|
|
|
+ ("This warns you if the glucose monitor stops sending readings for too long..", "当血糖仪停止发送读数太久时提醒你。"),
|
|
|
+ ("Warn me before the sensor’s %lld-day change-over.", "在传感器 %lld 天更换期前提醒我。"),
|
|
|
+])
|
|
|
+
|
|
|
+# ==== 三轮:护栏 / Nightscout 校验 / 杂项 ====
|
|
|
+add([
|
|
|
+ ("Value outside of guardrails: %@\n", "数值超出安全护栏:%@\n"),
|
|
|
+ ("Minimum: %@\n", "最小值:%@\n"),
|
|
|
+ ("Maximum: %@", "最大值:%@"),
|
|
|
+ ("The address is empty.", "地址为空。"),
|
|
|
+ ("The URL is invalid.", "网址无效。"),
|
|
|
+ ("A network error occurred.", "发生网络错误。"),
|
|
|
+ ("The site was not found.", "未找到该站点。"),
|
|
|
+ ("The token is invalid.", "令牌无效。"),
|
|
|
+ ("A token is required.", "需要令牌。"),
|
|
|
+ ("An unknown error occurred.", "发生未知错误。"),
|
|
|
+ ("Cone", "锥形区间"),
|
|
|
+ ("Lines", "线条"),
|
|
|
+ ("Static", "固定"),
|
|
|
+ ("Dynamic (BG Range)", "动态(按血糖范围)"),
|
|
|
+ ("Unknown Device", "未知设备"),
|
|
|
+ ("Expected bg delay: %@", "预计血糖延迟:%@"),
|
|
|
+ ("Temporary Override", "临时配置"),
|
|
|
+ ("Add the contact named '%@' to your watch face to show the current BG value in real time. Make sure to give the app full access to Contacts when prompted.",
|
|
|
+ "把名为「%@」的联系人添加到表盘,即可实时显示当前血糖。弹出授权时请允许应用完整访问通讯录。"),
|
|
|
+ ("BG Update Delay (Sec): %lld", "血糖更新延迟(秒):%lld"),
|
|
|
+])
|
|
|
+
|
|
|
+# ==== 三轮:导入/导出 ====
|
|
|
+add([
|
|
|
+ ("Scanning failed: %@", "扫描失败:%@"),
|
|
|
+ ("Invalid QR code data", "二维码数据无效"),
|
|
|
+ ("Failed to decode or migrate settings from QR code", "无法解析或迁移二维码中的设置"),
|
|
|
+ ("Import failed. This might be due to a version change (current: %@). Please try exporting settings from the source device again.",
|
|
|
+ "导入失败。可能是版本变更所致(当前版本:%@)。请在源设备上重新导出设置后再试。"),
|
|
|
+ ("Successfully imported: %@", "导入成功:%@"),
|
|
|
+ ("Please configure your Nightscout settings first (URL and Token)", "请先配置你的 Nightscout 设置(网址和令牌)"),
|
|
|
+ ("Please complete your Remote settings configuration (check required fields for %@)", "请完善你的远程设置(检查 %@ 的必填项)"),
|
|
|
+ ("Please configure your Remote settings first (select a remote type and configure required fields)", "请先配置你的远程设置(选择远程类型并填写必填项)"),
|
|
|
+ ("Please configure your Alarm settings first", "请先配置你的闹钟设置"),
|
|
|
+ ("Please configure your Dexcom Share settings first (username and password)", "请先配置你的 Dexcom Share 设置(用户名和密码)"),
|
|
|
+ ("The settings file exists but contains no valid settings to import.", "设置文件存在,但不包含可导入的有效设置。"),
|
|
|
+ ("Import failed: %@", "导入失败:%@"),
|
|
|
+ ("Remote type is changing from %@ to %@. This may affect your remote commands.\n", "远程类型将从 %1$@ 变更为 %2$@。这可能影响你的远程指令。\n"),
|
|
|
+ ("Remote user is changing from '%@' to '%@'. This may affect your remote commands.\n", "远程用户将从「%1$@」变更为「%2$@」。这可能影响你的远程指令。\n"),
|
|
|
+ ("APNS Key ID is changing. This may affect your remote commands.\n", "APNS 密钥 ID 即将变更。这可能影响你的远程指令。\n"),
|
|
|
+ ("APNS Key is changing. This may affect your remote commands.\n", "APNS 密钥即将变更。这可能影响你的远程指令。\n"),
|
|
|
+ ("Shared secret is changing. This may affect your remote commands.\n", "共享密钥即将变更。这可能影响你的远程指令。\n"),
|
|
|
+ ("Team ID is changing. This may affect your remote commands.\n", "团队 ID 即将变更。这可能影响你的远程指令。\n"),
|
|
|
+ ("Loop APNS QR Code URL is changing. This may affect your remote commands.\n", "Loop APNS 二维码网址即将变更。这可能影响你的远程指令。\n"),
|
|
|
+ ("Note: This import does not include Nightscout token settings. Your current Nightscout token will be preserved.\n",
|
|
|
+ "注意:此次导入不包含 Nightscout 令牌设置。你当前的 Nightscout 令牌将被保留。\n"),
|
|
|
+ ("Settings from version %@ may not be fully compatible with current version %@. Some features may not work as expected.",
|
|
|
+ "来自版本 %1$@ 的设置可能与当前版本 %2$@ 不完全兼容,部分功能可能无法正常工作。"),
|
|
|
+ ("Nightscout Settings", "Nightscout 设置"),
|
|
|
+ ("Dexcom Share Settings", "Dexcom Share 设置"),
|
|
|
+ ("Scan this QR code with another LoopFollow app to import %@", "用另一台 LoopFollow 扫描此二维码以导入%@"),
|
|
|
+ ("Export %@", "导出%@"),
|
|
|
+])
|
|
|
+
|
|
|
# ---------- 写出 ----------
|
|
|
-strings = collections.OrderedDict()
|
|
|
+if os.path.exists(OUT):
|
|
|
+ with open(OUT, "r", encoding="utf-8") as f:
|
|
|
+ catalog = json.load(f, object_pairs_hook=collections.OrderedDict)
|
|
|
+else:
|
|
|
+ catalog = collections.OrderedDict([
|
|
|
+ ("sourceLanguage", "en"),
|
|
|
+ ("strings", collections.OrderedDict()),
|
|
|
+ ("version", "1.0"),
|
|
|
+ ])
|
|
|
+
|
|
|
+strings = catalog.setdefault("strings", collections.OrderedDict())
|
|
|
for en in sorted(T.keys()):
|
|
|
zh = T[en]
|
|
|
- strings[en] = {
|
|
|
- "extractionState": "manual",
|
|
|
- "localizations": {
|
|
|
- "zh-Hans": {
|
|
|
- "stringUnit": {"state": "translated", "value": zh}
|
|
|
- }
|
|
|
- },
|
|
|
+ entry = strings.setdefault(en, collections.OrderedDict())
|
|
|
+ entry.setdefault("extractionState", "manual")
|
|
|
+ localizations = entry.setdefault("localizations", collections.OrderedDict())
|
|
|
+ localizations["zh-Hans"] = {
|
|
|
+ "stringUnit": {"state": "translated", "value": zh}
|
|
|
}
|
|
|
|
|
|
-catalog = collections.OrderedDict()
|
|
|
-catalog["sourceLanguage"] = "en"
|
|
|
-catalog["strings"] = strings
|
|
|
-catalog["version"] = "1.0"
|
|
|
-
|
|
|
os.makedirs(os.path.dirname(OUT), exist_ok=True)
|
|
|
with open(OUT, "w", encoding="utf-8") as f:
|
|
|
json.dump(catalog, f, ensure_ascii=False, indent=2)
|