Bladeren bron

Add Simplified Chinese localization and language picker support.

Introduce zh-Hans strings for UI, app name, menus, and Apple/Google/AI terminology; wire the splash and share sheet to the selected locale.

Co-authored-by: Cursor <cursoragent@cursor.com>
AhtashamShahzad1 1 maand geleden
bovenliggende
commit
9d2ee4a723

+ 5 - 0
Info.plist

@@ -8,6 +8,11 @@
 	<string>https://example.com/app-link-coming-soon</string>
 	<key>CFBundleDevelopmentRegion</key>
 	<string>$(DEVELOPMENT_LANGUAGE)</string>
+	<key>CFBundleLocalizations</key>
+	<array>
+		<string>en</string>
+		<string>zh-Hans</string>
+	</array>
 	<key>CFBundleDisplayName</key>
 	<string>AI Companion for Meet</string>
 	<key>CFBundleExecutable</key>

+ 1 - 0
meetings_app.xcodeproj/project.pbxproj

@@ -97,6 +97,7 @@
 			knownRegions = (
 				en,
 				Base,
+				"zh-Hans",
 			);
 			mainGroup = 13AF5D992F83DE22001BE867;
 			minimizedProjectReferenceProxies = 1;

+ 19 - 4
meetings_app/Localization/AppLanguageManager.swift

@@ -7,6 +7,9 @@ extension Notification.Name {
 enum AppLanguageManager {
     private static let defaultsKey = "settings.appLanguageCode"
 
+    /// UI languages shown in Settings → Language (order preserved).
+    private static let supportedLanguageCodes = ["en", "zh-Hans"]
+
     /// `nil` follows the system language; otherwise uses the selected `.lproj` code.
     static var selectedLanguageCode: String? {
         get {
@@ -24,9 +27,9 @@ enum AppLanguageManager {
     }
 
     static var availableLanguageCodes: [String] {
-        Bundle.main.localizations
-            .filter { $0 != "Base" }
-            .sorted { $0.localizedCaseInsensitiveCompare($1) == .orderedAscending }
+        supportedLanguageCodes.filter { code in
+            Bundle.main.path(forResource: code, ofType: "lproj") != nil
+        }
     }
 
     static var localizationBundle: Bundle {
@@ -45,8 +48,20 @@ enum AppLanguageManager {
         return .current
     }
 
+    /// Localized product name (splash, share sheet, etc.).
+    static var localizedAppDisplayName: String {
+        "AI Companion for Meet".localized
+    }
+
     static func displayName(for languageCode: String) -> String {
-        Locale.current.localizedString(forIdentifier: languageCode) ?? languageCode
+        switch languageCode {
+        case "en":
+            return "English"
+        case "zh-Hans":
+            return "简体中文"
+        default:
+            return Locale.current.localizedString(forIdentifier: languageCode) ?? languageCode
+        }
     }
 
     static func setLanguage(code: String?) {

+ 2 - 7
meetings_app/ViewController.swift

@@ -691,10 +691,7 @@ private extension ViewController {
         guard launchSplashView == nil else { return }
         let splash = LaunchSplashView(frame: .zero)
         splash.alphaValue = 1
-        let displayName = (Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String)?
-            .trimmingCharacters(in: .whitespacesAndNewlines)
-        let fallbackName = Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String
-        let resolvedName = (displayName?.isEmpty == false ? displayName : fallbackName) ?? "AI Companion for Meet"
+        let resolvedName = AppLanguageManager.localizedAppDisplayName
         let splashIcon = NSImage(named: "LaunchLogo") ?? NSApp.applicationIconImage
         splash.configure(appName: resolvedName, appIcon: splashIcon, theme: makeLaunchSplashTheme())
 
@@ -1218,9 +1215,7 @@ private extension ViewController {
     }
 
     private func shareAppFromSettingsMenu(sourceView: NSView? = nil, clickLocationInSourceView: NSPoint? = nil) {
-        let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String
-            ?? Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String
-            ?? "Meetings App".localized
+        let appName = AppLanguageManager.localizedAppDisplayName
         let message = "Check out %@ for managing and joining meetings.".localized(with: appName)
         let appURL = shareAppURL()
         let shareItems: [Any] = appURL.map { [message, $0] } ?? [message]

+ 1 - 0
meetings_app/en.lproj/Localizable.strings

@@ -1,4 +1,5 @@
 /* AppBrand.name ("Meetings") is intentionally omitted — the app name stays fixed in code. */
+"AI Companion for Meet" = "AI Companion for Meet";
 
 /* MARK: - Common */
 "OK" = "OK";

+ 4 - 0
meetings_app/zh-Hans.lproj/InfoPlist.strings

@@ -0,0 +1,4 @@
+"CFBundleDisplayName" = "Meet 智能助手";
+"CFBundleName" = "Meet 智能助手";
+"NSMicrophoneUsageDescription" = "经您同意,本应用会在本机录制会议音频,以便在会议结束后于智能助手中查看。";
+"NSSpeechRecognitionUsageDescription" = "本应用会将您保存的会议音频转换为文字转录,供智能助手使用。";

+ 451 - 0
meetings_app/zh-Hans.lproj/Localizable.strings

@@ -0,0 +1,451 @@
+/* AppBrand.name ("Meetings") 有意省略 — 应用名称在代码中保持不变。 */
+"AI Companion for Meet" = "Meet 智能助手";
+
+/* MARK: - 通用 */
+"OK" = "确定";
+"Cancel" = "取消";
+"Continue" = "继续";
+"Apply" = "应用";
+"Reset" = "重置";
+"Save" = "保存";
+"Copy" = "复制";
+"Copied" = "已复制";
+"Close" = "关闭";
+"Join" = "加入";
+"None" = "无";
+"Link" = "链接";
+"Stop" = "停止";
+"Address" = "地址";
+"Title" = "标题";
+"Notes" = "备注";
+"Start" = "开始";
+"Premium" = "高级版";
+"Popular" = "热门";
+"Browser" = "浏览器";
+"Meeting" = "会议";
+"Error" = "错误";
+"Success" = "成功";
+
+/* MARK: - 状态栏 */
+"Meetings status menu" = "会议状态菜单";
+"Open App" = "打开应用";
+"Join Meetings" = "加入会议";
+"Schedule" = "日程安排";
+"Calendar" = "日历";
+"Widgets" = "小组件";
+"AI Companion" = "智能助手";
+"Settings" = "设置";
+"Top meetings" = "置顶会议";
+"No upcoming meetings" = "没有即将开始的会议";
+"Sign Out" = "退出登录";
+"Sign In" = "登录";
+"Quit" = "退出";
+
+/* MARK: - 启动闪屏 */
+"Preparing your meetings workspace" = "正在准备您的会议工作区";
+"CONNECTING SERVICES" = "正在连接服务";
+"READY" = "就绪";
+"Plan smart, achieve more" = "智能规划,成就更多";
+
+/* MARK: - 小组件 */
+"Add Widget to Desktop" = "添加小组件到桌面";
+"Choose a size and add your Google Meet widget." = "选择尺寸并添加您的谷歌会议小组件。";
+"Tip: Right-click a desktop widget to remove it." = "提示:右键点击桌面小组件可将其移除。";
+"Removed %@ widget from desktop." = "已从桌面移除 %@ 小组件。";
+"Added %@ widget from desktop." = "已从桌面添加 %@ 小组件。";
+"Widgets are Premium only. Upgrade to add widgets." = "小组件仅限高级版。升级以添加小组件。";
+"Remove Widget" = "移除小组件";
+"Small" = "小";
+"Medium" = "中";
+"Quick actions" = "快速操作";
+"Open Meet" = "打开 Meet";
+"Upcoming meetings" = "即将开始的会议";
+"Sign in to see upcoming meetings" = "登录以查看即将开始的会议";
+"Sign in with Google" = "使用谷歌登录";
+"Google Meet" = "谷歌会议";
+"Open and join quickly" = "快速打开并加入";
+"Refresh" = "刷新";
+
+/* MARK: - 侧边栏与导航 */
+"Planning" = "规划";
+"Additional" = "更多";
+"Get Premium" = "获取高级版";
+"Manage Subscription" = "管理订阅";
+"App for Google Meet" = "谷歌会议应用";
+
+/* MARK: - 加入会议 */
+"New Instant Meet" = "新建即时会议";
+"Start instant Meet in more section with\nGoogle Meet meet." = "在更多选项中通过\n谷歌会议开始即时会议。";
+"Join with Link" = "通过链接加入";
+"Code or meet.google.com/…" = "代码或 meet.google.com/…";
+"Join with ID" = "通过 ID 加入";
+"Join with URL" = "通过 URL 加入";
+"Meeting URL" = "会议 URL";
+"Enter meeting URL..." = "输入会议 URL...";
+"Quick Join Meeting" = "快速加入会议";
+"Invalid Meet link" = "无效的 Meet 链接";
+"Enter a valid Google Meet link or meeting code (for example nkd-grps-duv, meet.google.com/nkd-grps-duv, or https://meet.google.com/nkd-grps-duv)." = "输入有效的谷歌会议链接或会议代码(例如 nkd-grps-duv、meet.google.com/nkd-grps-duv 或 https://meet.google.com/nkd-grps-duv)。";
+"Instant Meeting" = "即时会议";
+"Connect Google to start Instant Meeting" = "连接谷歌以开始即时会议";
+"To auto-stop recording when the meeting ends, connect your Google account first." = "要在会议结束时自动停止录制,请先连接您的谷歌帐户。";
+"Could not create Instant Meeting" = "无法创建即时会议";
+"Free trial used. Upgrade to continue." = "免费试用已用完。升级以继续。";
+
+/* MARK: - 浏览网页 */
+"Browse the web" = "浏览网页";
+"Enter a web address (for example meet.google.com)." = "输入网址(例如 meet.google.com)。";
+"Invalid address" = "无效地址";
+"Enter a valid http or https URL." = "输入有效的 http 或 https URL。";
+"Browse" = "浏览";
+"Quick links" = "快速链接";
+"Zoom help" = "Zoom 帮助";
+"https://example.com or example.com" = "https://example.com 或 example.com";
+"Enter a valid web address, for example https://example.com" = "输入有效网址,例如 https://example.com";
+"Address not allowed" = "地址不允许";
+"This URL is not permitted with the current in-app browser policy (whitelist)." = "当前应用内浏览器策略(白名单)不允许此 URL。";
+"Unable to load page" = "无法加载页面";
+"Could not load this page in the in-app browser.\n\n%@" = "无法在应用内浏览器中加载此页面。\n\n%@";
+"Page stopped loading" = "页面已停止加载";
+"Unable to open browser" = "无法打开浏览器";
+"Unable to Open Safari" = "无法打开 Safari";
+
+/* MARK: - 日程安排与日历 */
+"All" = "全部";
+"Today" = "今天";
+"This week" = "本周";
+"This month" = "本月";
+"Previous week" = "上一周";
+"Previous month" = "上一月";
+"Custom range" = "自定义范围";
+"No meetings match the selected filters." = "没有会议符合所选筛选条件。";
+"Adjust the date range and tap Apply." = "调整日期范围并点击“应用”。";
+"Premium required. Get Premium now to unlock Calendar." = "需要高级版。立即获取高级版以解锁日历。";
+"Premium required. Click to open paywall." = "需要高级版。点击打开付费页面。";
+"Get Premium to see events" = "获取高级版以查看事件";
+"Connect Google" = "连接谷歌";
+"Sign in with Google first to schedule a meeting from Calendar." = "请先使用谷歌登录,以便从日历安排会议。";
+"Select date" = "选择日期";
+"Choose a date to schedule your meeting." = "选择日期以安排您的会议。";
+"Google schedule" = "谷歌日程";
+"To show scheduled meetings, connect your Google account. You’ll need a Google OAuth client ID (Desktop) and a redirect URI matching the app’s callback scheme." = "要显示已安排的会议,请连接您的谷歌帐户。您需要谷歌 OAuth 客户端 ID(桌面版)和一个与应用回调方案匹配的重定向 URI。";
+"Actions" = "操作";
+"Schedule meeting" = "安排会议";
+"Team sync" = "团队同步";
+"Duration (min)" = "时长(分钟)";
+"Refresh saved meetings" = "刷新已保存的会议";
+
+/* MARK: - 设置 */
+"Manage appearance, account, and app options." = "管理外观、帐户和应用选项。";
+"Appearance" = "外观";
+"Account" = "帐户";
+"App" = "应用";
+"Notifications" = "通知";
+"Help & Legal" = "帮助与法律";
+"Dark Mode" = "深色模式";
+"Language" = "语言";
+"System Language" = "系统语言";
+"Rate Us" = "给我们评分";
+"Share App" = "分享应用";
+"Upgrade" = "升级";
+"Privacy Policy" = "隐私政策";
+"Support" = "支持";
+"Terms of Services" = "服务条款";
+"AI Language 1" = "智能语言 1";
+"AI Language 2" = "智能语言 2";
+"Google account connected" = "谷歌帐户已连接";
+"Google account not connected" = "谷歌帐户未连接";
+"Signed in" = "已登录";
+"Sign in to sync your meetings and calendar." = "登录以同步您的会议和日历。";
+"Log out" = "登出";
+"Check out %@ for managing and joining meetings." = "试试 %@ 来管理和加入会议。";
+"Meetings App" = "会议应用";
+
+/* MARK: - 录制同意 */
+"Record this meeting locally?" = "在本地录制此会议?";
+"With your consent, the app will record meeting audio on this Mac and show it in AI Companion after the meeting." = "经您同意,应用将在此 Mac 上录制会议音频,并在会议结束后在智能助手中显示。";
+"Allow and Continue" = "允许并继续";
+"Continue Without Recording" = "不录制继续";
+"Transcription Languages" = "转录语言";
+"Preferred Language 1" = "首选语言 1";
+"Preferred Language 2" = "首选语言 2";
+"Microphone permission denied" = "麦克风权限被拒绝";
+"Microphone permission required" = "需要麦克风权限";
+"Enable microphone access in System Settings to record meetings locally." = "请在系统设置中启用麦克风访问权限以在本地录制会议。";
+"Could not start recording" = "无法开始录制";
+"Recording will start when the meeting starts" = "录制将在会议开始时开始";
+"Meeting recording started (meeting + microphone)" = "会议录制已开始(会议 + 麦克风)";
+"System audio unavailable. Recording microphone only." = "系统音频不可用。仅录制麦克风。";
+"Meeting recording started (microphone only)" = "会议录制已开始(仅麦克风)";
+"Meeting recording saved" = "会议录制已保存";
+
+/* MARK: - 智能助手 */
+"Get notes from your previous meetings" = "获取您之前会议的笔记";
+"Connect your Google account to see your AI Companion meeting history." = "连接您的谷歌帐户以查看您的智能助手会议历史记录。";
+"No previous meetings yet for %@. End a meeting to save it here and generate notes." = "%@ 尚无之前的会议。结束会议可将其保存于此并生成笔记。";
+"this account" = "此帐户";
+"Previous meetings" = "之前的会议";
+"Recording in progress: %@" = "正在录制:%@";
+"Started: %@" = "开始时间:%@";
+"Stop Recording" = "停止录制";
+"Date: %@" = "日期:%@";
+"Saved time: %@" = "保存时间:%@";
+"Get Notes" = "获取笔记";
+"Premium required. Click to unlock AI Companion notes." = "需要高级版。点击解锁智能助手笔记。";
+"Play Audio" = "播放音频";
+"Pause Audio" = "暂停音频";
+"Stopped" = "已停止";
+"Speaking..." = "正在说话...";
+"Paused" = "已暂停";
+"Playing..." = "正在播放...";
+"Not playing" = "未播放";
+"Checking audio..." = "正在检查音频...";
+"Audio file missing" = "音频文件缺失";
+"Could not play recording" = "无法播放录制";
+"Failed: %@" = "失败:%@";
+"Failed to play audio" = "播放音频失败";
+"decode error" = "解码错误";
+"Loading..." = "加载中...";
+"Buffering..." = "缓冲中...";
+"Finished" = "已完成";
+"Transcript not requested" = "未请求转录";
+"Transcript processing..." = "转录处理中...";
+"Transcript ready" = "转录就绪";
+"Transcript unavailable (tap to retry)" = "转录不可用(点击重试)";
+"Notes not requested" = "未请求笔记";
+"Preparing notes, please wait..." = "正在准备笔记,请稍候...";
+"Notes ready" = "笔记就绪";
+"Notes unavailable (tap to retry)" = "笔记不可用(点击重试)";
+"Transcript unavailable" = "转录不可用";
+"Notes unavailable" = "笔记不可用";
+"Could not find recording details for this meeting." = "找不到此会议的录制详情。";
+"Transcript - %@" = "转录 - %@";
+"Notes - %@" = "笔记 - %@";
+"Meeting Notes" = "会议笔记";
+"Transcript processing..." = "转录处理中...";
+
+/* MARK: - 付费墙与 StoreKit */
+"Meetings Pro" = "会议专业版";
+"PREMIUM EXPERIENCE" = "高级体验";
+"Elevate your meetings workflow" = "提升您的会议工作流程";
+"Unlock automation, premium meeting tools, and priority support for every session." = "解锁自动化、高级会议工具以及每次会议的优先支持。";
+"Manage meetings" = "管理会议";
+"Smart scheduling" = "智能安排";
+"Faster workflow" = "更高效的工作流程";
+"Reminder notifications" = "提醒通知";
+"Monthly" = "月度";
+"Bi Yearly" = "半年度";
+"Yearly" = "年度";
+"Most Popular" = "最受欢迎";
+"%@ • Most Popular" = "%@ • 最受欢迎";
+"3 days free trial" = "3 天免费试用";
+"Cancel anytime" = "随时取消";
+"No lock-in" = "无合约限制";
+"Instant access" = "即时访问";
+"Unlock all tools" = "解锁所有工具";
+"Secure billing" = "安全计费";
+"Handled by Apple" = "由苹果处理";
+"Secured by Apple. Cancel anytime." = "由苹果提供安全保障。随时取消。";
+"Restore Purchase" = "恢复购买";
+"Continue with free plan" = "继续使用免费计划";
+"Already Premium" = "已是高级版";
+"You are already premium. Do you want to continue with this purchase?" = "您已是高级版。是否要继续此次购买?";
+"Current plan: %@. Choose a different package to switch." = "当前计划:%@。选择其他套餐以切换。";
+"Premium is active on this Apple ID." = "此苹果 ID 上高级版已激活。";
+"Free for %@, then %@" = "%@ 免费,之后 %@";
+"Billed via App Store" = "通过 App Store 计费";
+"%@  •  %@" = "%@  •  %@";
+"Save 0%" = "节省 0%";
+"Save %@%%" = "节省 %@%%";
+"month" = "月";
+"year" = "年";
+"%@ free trial" = "%@ 免费试用";
+"Processing..." = "处理中...";
+"Premium Active" = "高级版已激活";
+"Continue with 3 day trail" = "继续 3 天试用";
+"Please Wait" = "请稍候";
+"A purchase is already being processed." = "已有购买正在处理中。";
+"This Apple ID already has premium access." = "此苹果 ID 已拥有高级版访问权限。";
+"Purchase Complete" = "购买完成";
+"Premium has been unlocked successfully." = "高级版已成功解锁。";
+"Purchase Pending" = "购买待处理";
+"Your purchase is pending approval. You can continue once it completes." = "您的购买正在等待批准。完成后您可以继续。";
+"Product Not Available" = "产品不可用";
+"Unable to load this product. Check your StoreKit configuration and product IDs." = "无法加载此产品。请检查您的 StoreKit 配置和产品 ID。";
+"Already Purchased" = "已购买";
+"This plan is already active on your Apple ID." = "此计划已在您的苹果 ID 上激活。";
+"Purchase Failed" = "购买失败";
+"Restore Purchases" = "恢复购买";
+"Purchases restored successfully." = "购买恢复成功。";
+"No previous premium purchase was found for this Apple ID." = "未找到此苹果 ID 之前的任何高级版购买记录。";
+"Restore failed: %@" = "恢复失败:%@";
+"Unable to Open Subscriptions" = "无法打开订阅";
+"The subscriptions URL is invalid." = "订阅 URL 无效。";
+"%@ tapped." = "%@ 已点击。";
+
+/* MARK: - 认证 */
+"Google Sign-In" = "谷歌登录";
+
+/* MARK: - 语音识别/转录错误 */
+"Speech recognition permission denied. Enable it in System Settings and try again." = "语音识别权限被拒绝。请在系统设置中启用并重试。";
+"Speech recognition is restricted on this Mac." = "此 Mac 上语音识别受限。";
+"Speech recognition authorization is unavailable." = "语音识别授权不可用。";
+"Speech recognizer is unavailable for the current locale." = "当前区域设置下语音识别器不可用。";
+"Speech recognizer is unavailable for %@." = "%@ 的语音识别器不可用。";
+"No audio was available to transcribe." = "没有可用于转录的音频。";
+
+/* MARK: - StoreKit 交易错误 */
+"Unable to load products: %@" = "无法加载产品:%@";
+"Product ID not found in StoreKit response. Requested: %@. Loaded IDs: %@" = "StoreKit 响应中未找到产品 ID。请求:%@。已加载的 ID:%@";
+"Purchase verification failed." = "购买验证失败。";
+"Unknown purchase state." = "未知的购买状态。";
+"none" = "无";
+
+/* MARK: - 付费墙价格文案 */
+"%@ one-time purchase" = "%@ 一次性购买";
+"%@/month" = "%@/月";
+"%@/6 months" = "%@/6 个月";
+"%@/year (3 days free trial)" = "%@/年(3 天免费试用)";
+"day" = "天";
+"week" = "周";
+"period" = "周期";
+"1 %@" = "1 %@";
+"%d %@s" = "%d%@";
+
+/* MARK: - 提示与快速反馈 */
+"Meeting added successfully." = "会议添加成功。";
+"An issue occurred. Please try again." = "发生问题。请重试。";
+"Failed to load transcript." = "加载转录失败。";
+"Failed to generate notes." = "生成笔记失败。";
+
+/* MARK: - 浏览器提醒 */
+"Try Again" = "重试";
+"The in-app browser closed this page unexpectedly. You can try loading it again in this same window." = "应用内浏览器意外关闭了此页面。您可以尝试在同一窗口中重新加载。";
+
+/* MARK: - 日程安排与日历错误 */
+"Couldn't load schedule." = "无法加载日程。";
+"Couldn't refresh schedule." = "无法刷新日程。";
+"Couldn't connect Google account." = "无法连接谷歌帐户。";
+"Couldn't logout Google account." = "无法登出谷歌帐户。";
+"Couldn't load schedule" = "无法加载日程";
+"Couldn't determine Meet meeting code from URL." = "无法从 URL 确定 Meet 会议代码。";
+"Refreshing…" = "刷新中…";
+"Connect Google to see meetings" = "连接谷歌以查看会议";
+"Connect to load schedule" = "连接以加载日程";
+"Loading…" = "加载中…";
+"Loading schedule…" = "加载日程中…";
+"Start date must be on or before end date." = "开始日期必须早于或等于结束日期。";
+"Invalid custom date range" = "无效的自定义日期范围";
+"No meetings" = "无会议";
+"No meetings for selected filters" = "所选筛选条件无会议";
+"No meetings on %@" = "%@ 无会议";
+"1 meeting on %@" = "%@ 有 1 个会议";
+"%d meetings on %@" = "%@ 有 %d 个会议";
+"All day" = "全天";
+"Duration: all day" = "时长:全天";
+"Duration: %dh %dm" = "时长:%dh %dm";
+"Duration: %dh" = "时长:%dh";
+"Duration: %dm" = "时长:%dm";
+"%@ - %@" = "%@ - %@";
+"Scheduled Meeting" = "已安排的会议";
+
+/* MARK: - 创建会议弹窗 */
+"Please enter a title." = "请输入标题。";
+"Duration must be a positive number of minutes." = "时长必须为正数分钟。";
+"Invalid start time." = "无效的开始时间。";
+
+/* MARK: - 提醒与通知 */
+"Reminders" = "提醒";
+"1 Day Before" = "提前 1 天";
+"12 Hours Before" = "提前 12 小时";
+"1 Hour Before" = "提前 1 小时";
+"Starts in 1 day" = "1 天后开始";
+"Starts in 12 hours" = "12 小时后开始";
+"Starts in 1 hour" = "1 小时后开始";
+
+/* MARK: - 谷歌 OAuth 错误 */
+"Missing Google OAuth Client ID." = "缺少谷歌 OAuth 客户端 ID。";
+"Missing Google OAuth Client Secret." = "缺少谷歌 OAuth 客户端密钥。";
+"Invalid OAuth callback URL." = "无效的 OAuth 回调 URL。";
+"Google did not return an authorization code." = "谷歌未返回授权码。";
+"Token exchange failed: %@" = "令牌交换失败:%@";
+"Could not open browser for Google sign-in." = "无法打开浏览器进行谷歌登录。";
+"Google sign-in timed out." = "谷歌登录超时。";
+"No stored Google tokens found." = "未找到存储的谷歌令牌。";
+
+/* MARK: - 智能助手/转录错误 */
+"Recording not found." = "未找到录制。";
+"Local meeting audio is missing." = "本地会议音频缺失。";
+"Generated transcript was empty." = "生成的转录为空。";
+"(No transcript entries found.)" = "(未找到转录条目。)";
+"No conference record found for this meeting." = "未找到此会议的会议记录。";
+"No transcript found for this meeting." = "未找到此会议的转录。";
+"Unable to create audio composition tracks." = "无法创建音频合成轨道。";
+"Unable to create audio export session." = "无法创建音频导出会话。";
+"Audio mix export failed." = "音频混合导出失败。";
+"No display available for system audio capture." = "没有可用于系统音频捕获的显示器。";
+"Cannot add audio writer input." = "无法添加音频写入器输入。";
+
+/* MARK: - 谷歌 API 错误 */
+"Google Meet returned an invalid response." = "谷歌会议返回了无效响应。";
+"Google Meet API error (%d): %@" = "谷歌会议API 错误 (%d):%@";
+"Failed to parse Google Meet response: %@" = "解析谷歌会议响应失败:%@";
+"Google Calendar returned an invalid response." = "谷歌日历返回了无效响应。";
+"Google Calendar API error (%d): %@" = "谷歌日历 API 错误 (%d):%@";
+"Failed to parse Google Calendar events: %@" = "解析谷歌日历事件失败:%@";
+
+/* MARK: - 开放人工智能 错误 */
+"OpenAI API key is missing. Set OPENAI_API_KEY in the environment or app key vault." = "缺少开放人工智能 API 密钥。请在环境或应用密钥库中设置 OPENAI_API_KEY。";
+"Notes generation returned an invalid response." = "笔记生成返回了无效响应。";
+"Notes generation failed (%d): %@" = "笔记生成失败 (%d):%@";
+"Notes generation returned empty text." = "笔记生成返回了空文本。";
+"OpenAI transcription returned an invalid response." = "开放人工智能转录返回了无效响应。";
+"OpenAI transcription failed (%d): %@" = "开放人工智能转录失败 (%d):%@";
+"OpenAI transcription returned no text." = "开放人工智能转录未返回文本。";
+
+/* MARK: - 浏览与应用内浏览器 */
+"Open sites in the in-app browser (back, forward, reload, address bar). OAuth and “Continue in browser” flows stay inside the app." = "在应用内浏览器中打开网站(后退、前进、重新加载、地址栏)。OAuth 和“在浏览器中继续”流程保持在应用内。";
+"Open in app browser" = "在应用浏览器中打开";
+"Meet help" = "Meet 帮助";
+"Go" = "前往";
+"Back" = "返回";
+"Forward" = "前进";
+"Reload" = "重新加载";
+
+/* MARK: - 日程安排与日历标签 */
+"Google Calendar" = "谷歌日历";
+"Untitled meeting" = "无标题会议";
+"Created event missing start/end." = "创建的事件缺少开始/结束时间。";
+"Created event missing Meet/HTML link." = "创建的事件缺少 Meet/HTML 链接。";
+
+/* MARK: - 谷歌帐户 */
+"Google account" = "谷歌帐户";
+"%@, Google account" = "%@,谷歌帐户";
+
+/* MARK: - Zoom 加入(未使用部分) */
+"Meeting ID" = "会议 ID";
+"Enter meeting ID..." = "输入会议 ID...";
+"Meeting Passcode" = "会议密码";
+"Enter meeting passcode..." = "输入会议密码...";
+
+/* MARK: - 付费墙(备选权益) */
+"Virtual backgrounds" = "虚拟背景";
+"Tools for productivity" = "生产力工具";
+"24/7 support" = "7x24 小时支持";
+
+/* MARK: - 转录发言者 */
+"You" = "你";
+
+/* MARK: - 钥匙串错误 */
+"Failed to read OAuth tokens from Keychain." = "从钥匙串读取 OAuth 令牌失败。";
+"Keychain returned an invalid token payload." = "钥匙串返回了无效的令牌负载。";
+"Failed to update OAuth tokens in Keychain." = "更新钥匙串中的 OAuth 令牌失败。";
+"Failed to save OAuth tokens to Keychain." = "保存 OAuth 令牌到钥匙串失败。";
+"Failed to delete OAuth tokens from Keychain." = "从钥匙串删除 OAuth 令牌失败。";
+"Unable to allocate audio buffer." = "无法分配音频缓冲区。";
+"Invalid revoke response." = "无效的撤销响应。";
+"Token revoke failed with status %d." = "令牌撤销失败,状态码 %d。";
+
+/* MARK: - 错误提醒诊断 */
+"Keychain: %d (%@)" = "钥匙串:%d (%@)";
+"OSStatus: %d" = "操作系统状态:%d";
+"Item: service=%@, account=%@" = "项:服务=%@,帐户=%@";

+ 17 - 0
meetings_app/zh-Hans.lproj/Main.strings

@@ -0,0 +1,17 @@
+/* Class = "NSMenuItem"; title = "AI Companion for Meet"; ObjectID = "1Xt-HY-uBw"; */
+"1Xt-HY-uBw.title" = "Meet 智能助手";
+
+/* Class = "NSMenu"; title = "AI Companion for Meet"; ObjectID = "uQy-DD-JDr"; */
+"uQy-DD-JDr.title" = "Meet 智能助手";
+
+/* Class = "NSMenuItem"; title = "About AI Companion for Meet"; ObjectID = "5kV-Vb-QxS"; */
+"5kV-Vb-QxS.title" = "关于 Meet 智能助手";
+
+/* Class = "NSMenuItem"; title = "Hide AI Companion for Meet"; ObjectID = "Olw-nP-bQN"; */
+"Olw-nP-bQN.title" = "隐藏 Meet 智能助手";
+
+/* Class = "NSMenuItem"; title = "Quit AI Companion for Meet"; ObjectID = "4sb-4s-VLi"; */
+"4sb-4s-VLi.title" = "退出 Meet 智能助手";
+
+/* Class = "NSMenuItem"; title = "AI Companion for Meet Help"; ObjectID = "FKE-Sm-Kum"; */
+"FKE-Sm-Kum.title" = "Meet 智能助手帮助";