diff --git a/agent-ui/src/commonMain/composeResources/values-zh/strings.xml b/agent-ui/src/commonMain/composeResources/values-zh/strings.xml index 003616278a..0c2b79df39 100644 --- a/agent-ui/src/commonMain/composeResources/values-zh/strings.xml +++ b/agent-ui/src/commonMain/composeResources/values-zh/strings.xml @@ -37,4 +37,8 @@ 使用 Google 登录 正在等待浏览器 — 授权后请返回 CodeAssist 取消 + + CA 证书(可选) + + 仅当端点使用系统不信任的私有或区域性 CA 时(如 GigaChat)才粘贴 CA 证书(PEM)。它会在系统 CA 之外额外受信任;证书链仍会校验。 diff --git a/ide-ui/src/commonMain/composeResources/values-zh/strings.xml b/ide-ui/src/commonMain/composeResources/values-zh/strings.xml index 0fc41b6a9e..9b24b689d2 100644 --- a/ide-ui/src/commonMain/composeResources/values-zh/strings.xml +++ b/ide-ui/src/commonMain/composeResources/values-zh/strings.xml @@ -1267,4 +1267,863 @@ 变量 调用 注释 + + 关闭项目 + + 返回全部项目 + + 管理依赖项 + + 设置与工具 + + 设置 · 代码样式 · SDK 管理器 · 密钥库管理器 + + 图标管理器 + + 浏览并导入图标,更换应用图标 + + 查看日志 + + 编辑器、分析与构建日志——异常时分享排查 + + 模块 + + 添加/移除模块 · Java 版本 · 依赖项 · 仓库 + + 重建项目索引 + + 重建符号与补全索引 + + 切换主题 + + 在浅色与深色之间切换 + + 应用图标已更新 + + 应用图标 + + 背景 + + 选择一个图标 + + 选择一张图片 + + 当前图标 + + %1$d 个文件 + + 前景 + + Play 商店图(512px) + + 多密度 PNG(Android 8 之前) + + 圆形图标 + + 清单:%1$s + + 圆形 + + 圆角 + + 方形 + + 方圆形 + + 模块 + + 主题层 + + 此项目没有 Android 应用模块。 + + + + 水平移动 + + 垂直移动 + + 将写入的内容 + + 预览 + + 将替换 %1$d 个现有文件 + + 与前景相同 + + 缩放 + + 主题化 + + 应用图标 + + 使用项目 drawable + + 退出全屏 + + 全屏 + + 将 GitHub 或任意 Git 服务器上的仓库复制为新项目。 + + 从 Git 克隆 + + 返回搜索 + + BOM + + BoM + Analytics + + 融合定位 + + Google Maps SDK + + %1$s · %2$s + + 其他来源 + + Google 登录 + + 快速添加 + + 搜索或粘贴 group:name:version + + 作用域与变体选项 + + 本地 .jar 或 .aar 文件 + + 本项目中的另一个模块 + + +%1$s + + 详细信息 + + 构建输出、缓存和 SDK 绝不会打包。 + + Gradle 项目 + + 一个 .zip,包含你的源码和生成的 Gradle 构建文件,供 Android Studio 或 gradle 使用。 + + CodeAssist 安装包 + + 任何装有 CodeAssist 的人都能原样导入的 .caproj 文件。 + + 格式 + + 尽力而为。构建文件由项目模型生成,而非来自实际运行过的构建,因此请先通读再依赖它们。依赖项以 Maven 坐标声明,Gradle 在首次同步时下载,因此同步需要联网。 + + settings.gradle.kts、每个模块的 build.gradle.kts、gradle.properties 以及 Gradle wrapper 设置,全部由你的项目生成:模块、依赖项、SDK 版本、构建类型、变体和构建特性。 + + 会生成什么 + + 不包含 gradlew 脚本:Android Studio 会在首次同步时添加,或手动运行一次 gradle wrapper。 + + 将此项目写为 Gradle 构建,以便在 Android Studio 中打开或用 gradle 构建。 + + 排除一个模块时,依赖它的所有内容也会一并排除。 + + 模块 + + 这些内容也包含在压缩包内的 GRADLE-EXPORT.md 中。 + + 值得检查 + + 选项 + + 添加图片 + + 移除截图 + + 展示给导入此安装包的人。 + + 截图 + + 约 %1$s + + 你的 Gradle 项目已就绪。 + + AIDL 文件 + + 图片资源 + + FTP 服务器 + + 运行于 127.0.0.1:8021 · 上传到 assets/,此设备上的任何应用均可写入 + + 已停止 · 本地资源上传服务器 + + 转换 + + 转换 + + Gradle 构建文件会移至备份文件夹,由 CodeAssist 自己的模块系统接管。之后将无法再从 Gradle 重新同步。此操作可以撤销。 + + 完成 + + 无法转换此项目。 + + 导入器尽力读取了以下内容——转换后不会再检查: + + 转换为 CodeAssist 项目? + + 撤销 + + 正在转换… + + 已转换为 CodeAssist 项目。 + + 构建文件自上次同步后已更改。请重新同步以更新项目。 + + 发布会将你的项目放入商店,供任何人安装。以下是从头到尾的流程。 + + 每个压缩包 5 MB,最多 2000 个文件。每个账号可发布十个项目,同时最多三份提交等待审核。 + + 限制说明 + + 项目始终属于你。提交等待审核期间可以撤回,已发布的也可以申请下架。 + + 你的权益 + + 通过审核的项目会出现在"探索"中,任何人都可以安装。你可以继续发布新版本;旧版本仍然可用。 + + 通过后带着你的名字上架 + + 批准与否,决定都会以通知的形式送达,即使应用已关闭。 + + 无论结果如何都会通知你 + + 构建输出、缓存和 .git 不会包含在内。密钥也一样:keystore、local.properties、.env 和 google-services.json 都会被排除。上传前你可以看到确切的文件清单和被剔除的内容。 + + 项目在本设备上打包为压缩文件 + + 未经批准,任何内容都不会公开。审核员会下载压缩包、阅读你写的内容,然后选择发布或附上说明退回。 + + 会有真人进行审核 + + 发布流程是怎样的 + + 账号与工具 + + 备份项目 + + 取消 + + 从 Git 克隆 + + 删除 + + 删除项目 + + 这会从此设备上移除该项目及其全部内容。此操作无法撤销。 + + 删除 %1$s? + + 关闭 + + 此列表暂无内容 + + 发送反馈 + + 从商店模板开始 + + 下午好 + + 晚上好 + + 早上好 + + 导入项目 + + 旧版本的项目可以恢复。 + + 更多操作 + + 新建项目 + + 骨架在本地生成,无需联网。 + + 打开 + + 在文件管理中打开存储 + + 打开 %1$s + + 从上次离开的地方继续 + + 项目 + + 收藏 + + 更新 + + 设置与工具 + + 分享 + + 赞助 CodeAssist + + 去 GitHub 加星 + + 你的项目 + + 应用图标 + + 预览并替换启动器图标 + + 应用图标 + + 颜色 + + 保持原色 + + 添加 androidx.compose.material:material-icons-extended 即可浏览全部图标。 + + 此模块的类路径上没有 Compose 图标库。 + + %1$d 种配置 + + %1$s 已声明此资源。替换后所有引用都会更新。 + + 替换 %1$s? + + 已复制到剪贴板 + + 复制 + + 没有符合搜索条件的图标。 + + 填充 + + 添加到项目 + + 导入 SVG + + 插入到光标处 + + 许可协议:%1$s + + 加载 %1$s 图标 + + 正在下载图标列表… + + 资源名称 + + 尚未注册任何图标库。 + + 此项目还没有 drawable 或 mipmap 资源。 + + 引用 + + 替换 + + 文件夹 + + 搜索图标 + + 大小 + + 样式 + + 描边 + + 圆角 + + 直角 + + Compose + + 图标库 + + 项目 + + 写入位置 + + 图标管理器 + + 项目名称 + + 导入为 + + 将存放到 %1$s + + 兼容模式 + + 保留 Gradle 文件并从中重新同步。之后可以再转换。 + + 转换为 CodeAssist + + 切换到 CodeAssist 的模块系统。Gradle 文件会移至备份文件夹。 + + CodeAssist 应如何打开此项目? + + 导入 Gradle 项目 + + 隐藏文件 + + 显示全部 %1$d 个文件 + + 项目文件夹 + + CodeAssist 项目,或现有的 Gradle 构建 + + 项目在哪里? + + 项目安装包 + + 从 CodeAssist 分享或导出的 .caproj 文件 + + 导入项目 + + 已完成 %1$d 节课 + + %2$d 节课中的第 %1$d 节 · %3$d 分钟 + + 继续第 %1$d 节课 + + 点赞 + + 已点赞 + + 登录后点赞 + + 放弃 + + 有未保存的更改 + + Parcelable + + 新建 AIDL 文件 + + IMyService + + 清除 + + %1$d 天前 + + 关闭 + + 暂无新动态。 + + 审核结果、更新和完成的构建会显示在这里。 + + %1$d 小时前 + + 刚刚 + + 全部标为已读 + + %1$d 分钟前 + + 打开 + + 通知 + + 你将不会收到通知。 + + 刚才未能保存此设置。 + + 项目上架时会通知你。 + + 加载失败:%1$s + + 还没有安装任何插件。从应用商店安装的 CodeAssist 插件会显示在这里。 + + 内置 + + 已安装 + + 关注 + + %1$d 位关注者 + + 已关注 + + 安装量 + + 获赞 + + 没有这个名称的发布者 + + 还没有发布任何内容 + + 项目 + + 已发布的项目 + + 评分 + + 登录后关注 + + 未评分 + + 发布到商店 + + 分享给所有人,而不只是一个人。发布前会先经审核。 + + 你的回复会显示在评价下方,并标注为发布者。 + + 发布回复 + + 以发布者身份回复 + + 告诉我们它有什么问题。审核员会阅读每一份举报。 + + 还有什么需要审核员知道的吗?(可选) + + 错误或误导 + + 版权问题 + + 辱骂或不当内容 + + 恶意软件 + + 其他 + + 垃圾信息或广告 + + 发送举报 + + 正在发送… + + 举报此评价 + + 评价管理 + + 已隐藏。该评价不再计入评分。 + + 隐藏评价 + + 回复 + + 删除回复 + + 编辑回复 + + 举报 + + 已举报。审核员会查看。 + + 恢复评价 + + %1$d 条评价 + + 1 条评价 + + 删除 + + 编辑你的评价 + + 有帮助 + + 有帮助(%1$d) + + 暂无评价 + + 来当第一个说说这个项目用起来如何的人。 + + 于 v%1$s + + 有什么想告诉下一位用户的吗?(可选) + + 正在发布… + + 一位 CodeAssist 用户 + + 发布者已回复 + + 登录后投票 + + 登录后写评价 + + 最有帮助 + + 最新 + + %1$d 星(满分 5 星) + + 发布评价 + + 评分与评价 + + 评价功能暂时不可用。 + + 写评价 + + 你的评分 + + 你的评价 + + 横向滚动条 + + 当一行超出视图时,在底边显示可拖动的滚动条(自动换行开启时无需滚动) + + Android 应用 + + Android 库 + + Java 库 + + 查看原因 + + 使用 + + 查看列表页 + + 查看备注 + + 撤回 + + 已发布 %1$d 个项目 + + 书架第一 + + + + 按类型浏览 + + 随安装附带、可离线使用的项目骨架。 + + 内置于你的 IDE + + 最新优先 + + 精选集 + + %1$d%% + + 模板、示例应用和插件只要通过审核就会出现在这里。你可以成为第一个。 + + 还没有人发布项目 + + 项目积累一周的安装量后,排行榜就会出现。 + + 精选书架由团队从已发布的项目中挑选组成。 + + 需要 %2$d 个项目,已有 %1$d 个 + + 接下来会上架什么 + + 推荐需要你的安装历史——先安装一个项目吧。 + + 每个书架在内容足够参与排名时自动开启。 + + 商店成长后将陆续解锁 + + 排行榜需要至少 %1$d 个项目才启动——四个项目的榜单就是上面的列表。 + + 项目足够多、能凑出一个主题时,团队就会组建精选集。 + + 推荐需要跨越多个项目的安装历史。 + + 根据你的安装推荐… + + 下载量 + + 商店已开放 + + 安装量 + + 如何运作 + + 发布流程是怎样的 + + 安装 + + 安装项目 + + %1$d 条评价 + + 大小 + + 更新日志 + + 概览 + + README + + 评分与评价 + + 版本 + + 离线 + + 没有与"%1$s"匹配的项目 + + 暂无评分 + + 通知已关闭 + + 通知已开启 + + 只发一条通知,仅限首批上架。 + + 有项目上架时通知我 + + 打开 + + 在编辑器中打开 + + + + + + + + + + 与审核团队直接沟通 + + 发布首日即上门户首页 + + 现在已发布的所有项目都在这个页面上——没有要爬的排名,也没有藏在后面的分页。以后就不一定了。 + + 书架还没满 + + %1$s 下一个就是你的。 + + %1$s 你的将是第 %2$s 个。 + + 已收录 %1$d 个项目。 + + 已收录 1 个项目。 + + 再发布一个 + + 发布项目 + + 发布指南 + + 重试 + + 模板、示例应用、插件 + + 查看全部 + + 正在登录… + + 暂不 + + 登录未完成 + + 使用 GitHub 继续 + + 使用 Google 继续 + + 重新打开浏览器 + + 退出登录 + + 已登录为 %1$s + + 接下来是项目提交页。你的账号已就绪。 + + 发布到商店 + + 此构建版本未配置商店账号服务,因此发布功能已关闭。商店的其余功能均可正常使用。 + + 登录不可用 + + 在浏览器中完成登录后返回。此页面会自动更新。 + + 等待 GitHub + + 发布需要账号,这样项目才有归属者,审核员也有对象可沟通。浏览和安装则从不需要。 + + 正在干净的机器上构建 + + 需要修改——请查看备注 + + 已在商店上架 + + 未通过 + + 未通过 · %1$s + + 审核中 · 通常 2 个工作日 + + 标题、一句话简介、分类和截图——直接取自应用内的编辑器。 + + 填写列表信息 + + 选择任意本地项目,CodeAssist 会替你读取名称、语言、JDK 和 Gradle 配置——无需手写任何内容。 + + 从“项目”页选择一个项目 + + 我们会在干净的机器上构建,并由真人检查许可协议与 README。通常两个工作日。 + + 提交审核 + + 排行榜 + + 正在解压… + + 使用 + + 添加截图 + + 列表信息 + + 无需剔除任何内容。 + + 未包含在上传中 + + 密钥和构建输出绝不会被上传。以下文件已被识别并剔除: + + 分类 + + 描述 + + 一句话简介 + + 标签,逗号分隔 + + 标题 + + 版本 + + %1$d 个文件 · %2$s + + 你的提交 + + 你还没有可发布的项目。 + + 正在打包… + + 选择哪个项目? + + 请填写标题、简介、描述和分类。 + + 每份提交都会经过审核员审核后才会展示。未经批准,任何内容都不会公开。 + + 截图 + + 最多六张。这是别人在安装之前最先看的东西。 + + 提交审核 + + 正在提交… + + 登录后才能发布。 + + 发布项目 + + 此构建版本不支持发布。 + + 撤回 + + 在版本匹配之前,此模块的构建会一直失败。 + + 仍然构建 + + %1$d 个模块的运行时与内置的处理器版本不一致 + + 处理中… + + 克隆自 %1$s + + 仅以编辑方式打开 + + + %1$d 个项目 + %1$d 个项目 + diff --git a/ide-ui/src/commonMain/composeResources/values/strings.xml b/ide-ui/src/commonMain/composeResources/values/strings.xml index d2594b1828..028c8977e3 100644 --- a/ide-ui/src/commonMain/composeResources/values/strings.xml +++ b/ide-ui/src/commonMain/composeResources/values/strings.xml @@ -1604,4 +1604,163 @@ We never collect your code, file or project names, which features you use, or an Reference App icon No icon libraries are registered. + + Close project + + Back to all projects + + Manage dependencies + + Settings & Tools + + Settings · code style · SDK manager · keystore manager + + Icon Manager + + Browse and import icons, and change the app icon + + View logs + + Editor, analysis & build logs — share when something's off + + Modules + + Add/remove modules · Java version · dependencies · repositories + + Re-index project + + Rebuild symbol & completion indexes + + Toggle theme + + Switch between light and dark + + Read why + + Use + + View listing + + View notes + + Withdraw + + %1$d projects published + + FIRST ON THE SHELF + + NEW + + Offline scaffolds that ship with the install. + + Bundled with your IDE + + newest first + + Collections + + Templates, sample apps and plugins show up here the moment they pass review. Yours can be the first one. + + No one has published a project yet + + Ranks appear once projects have a week of install history. + + Curated shelves are assembled by the team from published projects. + + %1$d of %2$d projects needed + + What lands here next + + Recommendations need your install history — install something first. + + Each shelf switches on when it has enough to rank. + + What unlocks as the store grows + + Ranking starts at %1$d projects — a chart of four is just the list above. + + The team builds a collection once there are enough projects to theme one. + + Recommendations need install history across several projects. + + Because you installed… + + The store is open + + How it works + + How publishing works + + offline + + Not rated yet + + Notifications off + + Notifications on + + One notification, only for the first batch. + + Tell me when projects arrive + + fifth + + fourth + + second + + third + + Direct line to the review team + + Front page from day one + + Everything published right now sits on this page — no ranking to climb, no back pages to fall into. That stops being true later. + + Shelves are still short + + %1$s Yours would be next. + + %1$s Yours would be the %2$s. + + %1$d projects in. + + One project in. + + Publish another + + Publish a project + + Publishing guide + + Building on a clean machine + + Changes requested — read the notes + + Live in the store + + Not accepted + + Not accepted · %1$s + + In review · usually 2 business days + + Title, a short description, category and screenshots — captured from your editor inside the app. + + Fill in the listing + + Choose any local project. CodeAssist reads its name, language, JDK and Gradle setup for you — nothing to write by hand. + + Pick a project from Home + + We build it on a clean machine and a human checks licensing and the README. Usually two business days. + + Submit for review + + Top charts + + + %1$d PROJECT + %1$d PROJECTS + diff --git a/ide-ui/src/commonMain/kotlin/dev/ide/ui/components/StoreEmpty.kt b/ide-ui/src/commonMain/kotlin/dev/ide/ui/components/StoreEmpty.kt index 5698214292..87c943da61 100644 --- a/ide-ui/src/commonMain/kotlin/dev/ide/ui/components/StoreEmpty.kt +++ b/ide-ui/src/commonMain/kotlin/dev/ide/ui/components/StoreEmpty.kt @@ -39,6 +39,37 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import dev.ide.ui.backend.UiStoreSubmission import dev.ide.ui.backend.UiSubmissionStatus +import dev.ide.ui.generated.resources.Res +import dev.ide.ui.generated.resources.store_empty_hero_body +import dev.ide.ui.generated.resources.store_empty_hero_title +import dev.ide.ui.generated.resources.store_ghost_charts_note +import dev.ide.ui.generated.resources.store_ghost_collections_note +import dev.ide.ui.generated.resources.store_ghost_recommend_note +import dev.ide.ui.generated.resources.store_hero_eyebrow_open +import dev.ide.ui.generated.resources.store_notify_state_off +import dev.ide.ui.generated.resources.store_notify_state_on +import dev.ide.ui.generated.resources.store_notify_subtitle +import dev.ide.ui.generated.resources.store_notify_title +import dev.ide.ui.generated.resources.store_publish_another +import dev.ide.ui.generated.resources.store_publish_cta +import dev.ide.ui.generated.resources.store_publishing_guide +import dev.ide.ui.generated.resources.store_action_read_why +import dev.ide.ui.generated.resources.store_action_view_listing +import dev.ide.ui.generated.resources.store_action_view_notes +import dev.ide.ui.generated.resources.store_action_withdraw +import dev.ide.ui.generated.resources.store_status_building +import dev.ide.ui.generated.resources.store_status_changes +import dev.ide.ui.generated.resources.store_status_published +import dev.ide.ui.generated.resources.store_status_rejected +import dev.ide.ui.generated.resources.store_status_rejected_note +import dev.ide.ui.generated.resources.store_status_submitted +import dev.ide.ui.generated.resources.store_step_listing_body +import dev.ide.ui.generated.resources.store_step_listing_title +import dev.ide.ui.generated.resources.store_step_pick_body +import dev.ide.ui.generated.resources.store_step_pick_title +import dev.ide.ui.generated.resources.store_step_submit_body +import dev.ide.ui.generated.resources.store_step_submit_title +import org.jetbrains.compose.resources.stringResource import dev.ide.ui.icons.CaSymbols import dev.ide.ui.theme.Symbol import dev.ide.ui.theme.tileShape @@ -85,9 +116,9 @@ fun EmptyStoreHero( modifier = Modifier.align(Alignment.BottomEnd).offset(x = 24.dp, y = 34.dp), ) Column(Modifier.padding(horizontal = 20.dp, vertical = 24.dp)) { - Eyebrow("The store is open", color = c.onPrimaryContainer.copy(alpha = 0.75f)) + Eyebrow(stringResource(Res.string.store_hero_eyebrow_open), color = c.onPrimaryContainer.copy(alpha = 0.75f)) Text( - "No one has published a project yet", + stringResource(Res.string.store_empty_hero_title), style = MaterialTheme.typography.headlineMedium.copy( fontWeight = FontWeight.Medium, fontSize = 26.sp, lineHeight = 32.sp, letterSpacing = (-0.7).sp, @@ -96,8 +127,7 @@ fun EmptyStoreHero( modifier = Modifier.padding(top = 8.dp), ) Text( - "Templates, sample apps and plugins show up here the moment they pass review. " + - "Yours can be the first one.", + stringResource(Res.string.store_empty_hero_body), style = MaterialTheme.typography.bodyMedium, color = c.onPrimaryContainer.copy(alpha = 0.82f), modifier = Modifier.padding(top = 10.dp), @@ -121,7 +151,11 @@ fun EmptyStoreHero( ) { Symbol(CaSymbols.upload, contentDescription = null, size = 20.dp) Text( - if (hasSubmission) "Publish another" else "Publish a project", + if (hasSubmission) { + stringResource(Res.string.store_publish_another) + } else { + stringResource(Res.string.store_publish_cta) + }, style = MaterialTheme.typography.labelLarge, ) } @@ -135,7 +169,7 @@ fun EmptyStoreHero( modifier = Modifier.height(46.dp), ) { Box(Modifier.padding(horizontal = 20.dp), contentAlignment = Alignment.Center) { - Text("Publishing guide", style = MaterialTheme.typography.labelLarge) + Text(stringResource(Res.string.store_publishing_guide), style = MaterialTheme.typography.labelLarge) } } } @@ -189,19 +223,19 @@ fun PublishingSteps(steps: List, modifier: Modifier = Modifier) { } /** The default copy for [PublishingSteps]. Kept beside the component so the two cannot drift apart. */ -val defaultPublishSteps: List = listOf( +@Composable +fun defaultPublishSteps(): List = listOf( PublishStep( - "Pick a project from Home", - "Choose any local project. CodeAssist reads its name, language, JDK and Gradle setup for you — " + - "nothing to write by hand.", + stringResource(Res.string.store_step_pick_title), + stringResource(Res.string.store_step_pick_body), ), PublishStep( - "Fill in the listing", - "Title, a short description, category and screenshots — captured from your editor inside the app.", + stringResource(Res.string.store_step_listing_title), + stringResource(Res.string.store_step_listing_body), ), PublishStep( - "Submit for review", - "We build it on a clean machine and a human checks licensing and the README. Usually two business days.", + stringResource(Res.string.store_step_submit_title), + stringResource(Res.string.store_step_submit_body), ), ) @@ -225,6 +259,9 @@ fun NotifySwitchRow( message: String? = null, ) { val c = MaterialTheme.colorScheme + // Resolved outside the semantics block: stringResource is composable-only. + val stateOn = stringResource(Res.string.store_notify_state_on) + val stateOff = stringResource(Res.string.store_notify_state_off) Surface( shape = RoundedCornerShape(26.dp), color = c.surfaceContainerLow, @@ -238,12 +275,12 @@ fun NotifySwitchRow( ) { Column(Modifier.weight(1f)) { Text( - "Tell me when projects arrive", + stringResource(Res.string.store_notify_title), style = MaterialTheme.typography.titleSmall, color = c.onSurface, ) Text( - message ?: "One notification, only for the first batch.", + message ?: stringResource(Res.string.store_notify_subtitle), style = MaterialTheme.typography.bodySmall, // The failure takes over the supporting line rather than adding a row: it replaces the // promise it just failed to keep. @@ -257,7 +294,7 @@ fun NotifySwitchRow( checked = checked, onCheckedChange = onCheckedChange, modifier = Modifier.semantics { - stateDescription = if (checked) "Notifications on" else "Notifications off" + stateDescription = if (checked) stateOn else stateOff }, ) } @@ -282,19 +319,36 @@ fun SubmissionStatusCard( val c = MaterialTheme.colorScheme val (line, actionLabel, action) = when (submission.status) { UiSubmissionStatus.SUBMITTED -> - Triple("In review · usually 2 business days", "Withdraw", onWithdraw) + Triple( + stringResource(Res.string.store_status_submitted), + stringResource(Res.string.store_action_withdraw), + onWithdraw, + ) UiSubmissionStatus.BUILDING -> - Triple("Building on a clean machine", "Withdraw", onWithdraw) + Triple( + stringResource(Res.string.store_status_building), + stringResource(Res.string.store_action_withdraw), + onWithdraw, + ) UiSubmissionStatus.CHANGES_REQUESTED -> - Triple("Changes requested — read the notes", "View notes", onViewNotes) + Triple( + stringResource(Res.string.store_status_changes), + stringResource(Res.string.store_action_view_notes), + onViewNotes, + ) UiSubmissionStatus.REJECTED -> Triple( - submission.note?.let { "Not accepted · $it" } ?: "Not accepted", - "Read why", + submission.note?.let { stringResource(Res.string.store_status_rejected_note, it) } + ?: stringResource(Res.string.store_status_rejected), + stringResource(Res.string.store_action_read_why), onViewNotes, ) UiSubmissionStatus.PUBLISHED -> - Triple("Live in the store", "View listing", onViewListing) + Triple( + stringResource(Res.string.store_status_published), + stringResource(Res.string.store_action_view_listing), + onViewListing, + ) } // Only the in-flight states spin; a finished one holding an animation would read as still working. val spinning = submission.status == UiSubmissionStatus.SUBMITTED || @@ -376,8 +430,9 @@ private fun SpinningGlyph(glyph: Char, spinning: Boolean, tint: Color) { * Different from the sparse state's on purpose: with nothing published, the honest thing to state is the * **condition** that fills the shelf, not a threshold the reader is nowhere near. */ +@Composable fun emptyGhostNote(key: String): String = when (key) { - "charts" -> "Ranks appear once projects have a week of install history." - "collections" -> "Curated shelves are assembled by the team from published projects." - else -> "Recommendations need your install history — install something first." + "charts" -> stringResource(Res.string.store_ghost_charts_note) + "collections" -> stringResource(Res.string.store_ghost_collections_note) + else -> stringResource(Res.string.store_ghost_recommend_note) } diff --git a/ide-ui/src/commonMain/kotlin/dev/ide/ui/components/StoreSparse.kt b/ide-ui/src/commonMain/kotlin/dev/ide/ui/components/StoreSparse.kt index 6e2765e2fe..80f265d21c 100644 --- a/ide-ui/src/commonMain/kotlin/dev/ide/ui/components/StoreSparse.kt +++ b/ide-ui/src/commonMain/kotlin/dev/ide/ui/components/StoreSparse.kt @@ -36,6 +36,30 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import dev.ide.ui.backend.UiGhostShelf import dev.ide.ui.backend.UiStoreItem +import dev.ide.ui.generated.resources.Res +import dev.ide.ui.generated.resources.store_action_use +import dev.ide.ui.generated.resources.store_badge_cd +import dev.ide.ui.generated.resources.store_badge_first +import dev.ide.ui.generated.resources.store_badge_new +import dev.ide.ui.generated.resources.store_ghost_count_cd +import dev.ide.ui.generated.resources.store_how_it_works +import dev.ide.ui.generated.resources.store_not_rated +import dev.ide.ui.generated.resources.store_ordinal_fifth +import dev.ide.ui.generated.resources.store_ordinal_fourth +import dev.ide.ui.generated.resources.store_ordinal_second +import dev.ide.ui.generated.resources.store_ordinal_third +import dev.ide.ui.generated.resources.store_perk_direct_line +import dev.ide.ui.generated.resources.store_perk_front_page +import dev.ide.ui.generated.resources.store_pitch_body +import dev.ide.ui.generated.resources.store_pitch_eyebrow +import dev.ide.ui.generated.resources.store_pitch_next +import dev.ide.ui.generated.resources.store_pitch_ordinal_full +import dev.ide.ui.generated.resources.store_pitch_subject +import dev.ide.ui.generated.resources.store_pitch_subject_one +import dev.ide.ui.generated.resources.store_project_count +import dev.ide.ui.generated.resources.store_publish_cta +import org.jetbrains.compose.resources.pluralStringResource +import org.jetbrains.compose.resources.stringResource import dev.ide.ui.icons.CaSymbols import dev.ide.ui.theme.Symbol import dev.ide.ui.theme.cardShape @@ -59,18 +83,18 @@ import dev.ide.ui.theme.tonalPair @Composable fun StoreCountBadge(count: Int, modifier: Modifier = Modifier) { val c = MaterialTheme.colorScheme + // Resolved outside the semantics block: stringResource is composable-only. + val cd = stringResource(Res.string.store_badge_cd, count) + val label = pluralStringResource(Res.plurals.store_project_count, count, count) Surface( shape = RoundedCornerShape(9.dp), color = c.surfaceContainerHigh, contentColor = c.onSurfaceVariant, modifier = modifier.height(28.dp) - .semantics { contentDescription = "$count projects published" }, + .semantics { contentDescription = cd }, ) { Box(Modifier.padding(horizontal = 12.dp), contentAlignment = Alignment.Center) { - Text( - if (count == 1) "1 PROJECT" else "$count PROJECTS", - style = MaterialTheme.typography.labelSmall, - ) + Text(label, style = MaterialTheme.typography.labelSmall) } } } @@ -232,7 +256,7 @@ private fun RatingLine(item: UiStoreItem, onContainer: Color) { ) } else { Text( - "Not rated yet", + stringResource(Res.string.store_not_rated), style = MaterialTheme.typography.labelLarge, color = onContainer.copy(alpha = 0.75f), ) @@ -248,9 +272,10 @@ private fun RatingLine(item: UiStoreItem, onContainer: Color) { * * Index 0 is the shelf's first project; index 1 gets "NEW" only when it is genuinely recent. */ +@Composable private fun sparseBadge(index: Int, isRecent: Boolean): String? = when { - index == 0 -> "FIRST ON THE SHELF" - index == 1 && isRecent -> "NEW" + index == 0 -> stringResource(Res.string.store_badge_first) + index == 1 && isRecent -> stringResource(Res.string.store_badge_new) else -> null } @@ -284,7 +309,7 @@ fun PublishPitchBand( modifier = Modifier.align(Alignment.BottomEnd).offset(x = 20.dp, y = 26.dp), ) Column(Modifier.padding(20.dp)) { - Eyebrow("Shelves are still short", color = c.onPrimaryContainer.copy(alpha = 0.75f)) + Eyebrow(stringResource(Res.string.store_pitch_eyebrow), color = c.onPrimaryContainer.copy(alpha = 0.75f)) Text( pitchHeadline(projectCount), style = MaterialTheme.typography.headlineSmall.copy( @@ -295,8 +320,7 @@ fun PublishPitchBand( modifier = Modifier.padding(top = 8.dp), ) Text( - "Everything published right now sits on this page — no ranking to climb, no back " + - "pages to fall into. That stops being true later.", + stringResource(Res.string.store_pitch_body), style = MaterialTheme.typography.bodyMedium, color = c.onPrimaryContainer.copy(alpha = 0.82f), modifier = Modifier.padding(top = 8.dp), @@ -319,7 +343,7 @@ fun PublishPitchBand( horizontalArrangement = Arrangement.spacedBy(8.dp), ) { Symbol(CaSymbols.upload, contentDescription = null, size = 19.dp) - Text("Publish a project", style = MaterialTheme.typography.labelLarge) + Text(stringResource(Res.string.store_publish_cta), style = MaterialTheme.typography.labelLarge) } } Surface( @@ -331,7 +355,7 @@ fun PublishPitchBand( modifier = Modifier.height(44.dp), ) { Box(Modifier.padding(horizontal = 18.dp), contentAlignment = Alignment.Center) { - Text("How it works", style = MaterialTheme.typography.labelLarge) + Text(stringResource(Res.string.store_how_it_works), style = MaterialTheme.typography.labelLarge) } } } @@ -341,8 +365,8 @@ fun PublishPitchBand( Modifier.fillMaxWidth().padding(top = 18.dp), horizontalArrangement = Arrangement.spacedBy(10.dp), ) { - PerkTile(CaSymbols.visibility, "Front page from day one", Modifier.weight(1f)) - PerkTile(CaSymbols.forum, "Direct line to the review team", Modifier.weight(1f)) + PerkTile(CaSymbols.visibility, stringResource(Res.string.store_perk_front_page), Modifier.weight(1f)) + PerkTile(CaSymbols.forum, stringResource(Res.string.store_perk_direct_line), Modifier.weight(1f)) } } } @@ -373,16 +397,19 @@ private fun PerkTile(glyph: Char, label: String, modifier: Modifier = Modifier) * Above five the ordinal starts sounding like a small club rather than an opportunity, so it switches to * "Yours would be next" — the handoff's own note on the copy. */ +@Composable internal fun pitchHeadline(count: Int): String { - val subject = if (count == 1) "One project in." else "$count projects in." + val subject = if (count == 1) stringResource(Res.string.store_pitch_subject_one) + else stringResource(Res.string.store_pitch_subject, count) val ordinal = when (count) { - 1 -> "second" - 2 -> "third" - 3 -> "fourth" - 4 -> "fifth" + 1 -> stringResource(Res.string.store_ordinal_second) + 2 -> stringResource(Res.string.store_ordinal_third) + 3 -> stringResource(Res.string.store_ordinal_fourth) + 4 -> stringResource(Res.string.store_ordinal_fifth) else -> null } - return if (ordinal != null) "$subject Yours would be the $ordinal." else "$subject Yours would be next." + return if (ordinal != null) stringResource(Res.string.store_pitch_ordinal_full, subject, ordinal) + else stringResource(Res.string.store_pitch_next, subject) } /** @@ -423,12 +450,14 @@ fun GhostShelfCard( modifier = Modifier.weight(1f), ) if (shelf != null) { + // Resolved outside the semantics block: stringResource is composable-only. + val shelfCountCd = stringResource(Res.string.store_ghost_count_cd, shelf.have, shelf.need) Surface( shape = RoundedCornerShape(8.dp), color = c.surfaceContainer, contentColor = c.onSurfaceVariant, modifier = Modifier.height(24.dp).semantics { - contentDescription = "${shelf.have} of ${shelf.need} projects needed" + contentDescription = shelfCountCd }, ) { Box(Modifier.padding(horizontal = 10.dp), contentAlignment = Alignment.Center) { @@ -497,7 +526,7 @@ fun BundledTemplateRow( } Surface(shape = CircleShape, color = c.primaryContainer, contentColor = c.onPrimaryContainer) { Box(Modifier.height(34.dp).padding(horizontal = 15.dp), contentAlignment = Alignment.Center) { - Text("Use", style = MaterialTheme.typography.labelMedium) + Text(stringResource(Res.string.store_action_use), style = MaterialTheme.typography.labelMedium) } } } diff --git a/ide-ui/src/commonMain/kotlin/dev/ide/ui/screens/EditorSheets.kt b/ide-ui/src/commonMain/kotlin/dev/ide/ui/screens/EditorSheets.kt index cd40f51d60..c11f03fefa 100644 --- a/ide-ui/src/commonMain/kotlin/dev/ide/ui/screens/EditorSheets.kt +++ b/ide-ui/src/commonMain/kotlin/dev/ide/ui/screens/EditorSheets.kt @@ -151,7 +151,7 @@ internal fun MoreSheetContent( Text(stringResource(Res.string.more), color = MaterialTheme.colorScheme.onSurface, style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.SemiBold, modifier = Modifier.padding(start = 6.dp, top = 4.dp, bottom = 10.dp)) actions.forEach { a -> - MoreRow(actionIcon(a.iconId), a.text, a.description ?: "") { a.perform(host) } + MoreRow(actionIcon(a.iconId), localizedUiActionText(a), localizedUiActionDescription(a) ?: "") { a.perform(host) } } // Performance-analytics opt-in lives here (a settings surface) rather than on the home screen, so it's diff --git a/ide-ui/src/commonMain/kotlin/dev/ide/ui/screens/ExploreFeedScreen.kt b/ide-ui/src/commonMain/kotlin/dev/ide/ui/screens/ExploreFeedScreen.kt index 3492c38d38..73c19f3752 100644 --- a/ide-ui/src/commonMain/kotlin/dev/ide/ui/screens/ExploreFeedScreen.kt +++ b/ide-ui/src/commonMain/kotlin/dev/ide/ui/screens/ExploreFeedScreen.kt @@ -37,6 +37,20 @@ import dev.ide.ui.backend.UiStoreFeed import dev.ide.ui.backend.UiStoreItem import dev.ide.ui.backend.UiStoreMode import dev.ide.ui.backend.UiStorePublisher +import dev.ide.ui.generated.resources.store_bundled_subtitle +import dev.ide.ui.generated.resources.store_bundled_title +import dev.ide.ui.generated.resources.store_catalogue_subtitle +import dev.ide.ui.generated.resources.store_collections_title +import dev.ide.ui.generated.resources.store_ghost_next_title +import dev.ide.ui.generated.resources.store_ghost_section_subtitle +import dev.ide.ui.generated.resources.store_ghost_section_title +import dev.ide.ui.generated.resources.store_ghostspec_charts_note +import dev.ide.ui.generated.resources.store_ghostspec_collections_note +import dev.ide.ui.generated.resources.store_ghostspec_rec_note +import dev.ide.ui.generated.resources.store_ghostspec_rec_title +import dev.ide.ui.generated.resources.store_how_publishing_title +import dev.ide.ui.generated.resources.store_meta_offline +import dev.ide.ui.generated.resources.store_top_charts import dev.ide.ui.generated.resources.store_signin_title import dev.ide.ui.components.SquareToneButton import dev.ide.ui.components.inFlight @@ -64,6 +78,7 @@ import dev.ide.ui.components.StoreCountBadge import dev.ide.ui.components.TrendingTicker import dev.ide.ui.components.chartMeta import dev.ide.ui.components.motifFor +import org.jetbrains.compose.resources.stringResource import dev.ide.ui.icons.CaSymbols import dev.ide.ui.theme.tonalPair import dev.ide.ui.generated.resources.Res @@ -210,7 +225,7 @@ fun ExploreFeed( is UiFeedSection.Catalogue -> { item("head_${section.id}") { - SectionHeader(section.title, subtitle = "newest first") + SectionHeader(section.title, subtitle = stringResource(Res.string.store_catalogue_subtitle)) } itemsIndexed(section.items, key = { _, it -> "cat_${it.id}" }) { i, item -> Spacer(Modifier.height(12.dp)) @@ -243,15 +258,15 @@ fun ExploreFeed( if (bundled.isNotEmpty()) { item("head_${section.id}") { SectionHeader( - "Bundled with your IDE", - subtitle = "Offline scaffolds that ship with the install.", + stringResource(Res.string.store_bundled_title), + subtitle = stringResource(Res.string.store_bundled_subtitle), ) } itemsIndexed(bundled, key = { _, it -> "bundled_${it.id}" }) { i, item -> Spacer(Modifier.height(10.dp)) BundledTemplateRow( title = item.title, - meta = listOfNotNull(item.language, "offline").joinToString(" · "), + meta = listOfNotNull(item.language, stringResource(Res.string.store_meta_offline)).joinToString(" · "), iconId = item.iconId, index = i, onUse = { onUseBundled(item) }, @@ -263,8 +278,8 @@ fun ExploreFeed( is UiFeedSection.GhostShelves -> { item("head_${section.id}") { SectionHeader( - "What unlocks as the store grows", - subtitle = "Each shelf switches on when it has enough to rank.", + stringResource(Res.string.store_ghost_section_title), + subtitle = stringResource(Res.string.store_ghost_section_subtitle), ) } itemsIndexed(section.shelves, key = { _, it -> "ghost_${it.key}" }) { _, shelf -> @@ -372,7 +387,7 @@ private fun ChartsSection( horizontalArrangement = Arrangement.spacedBy(10.dp), ) { Text( - "Top charts", + stringResource(Res.string.store_top_charts), style = MaterialTheme.typography.headlineSmall, color = MaterialTheme.colorScheme.onSurface, modifier = Modifier.weight(1f), @@ -481,24 +496,25 @@ private data class GhostSpec( val slotCount: Int, ) +@Composable private fun ghostSpec(key: String, need: Int): GhostSpec = when (key) { "charts" -> GhostSpec( - title = "Top charts", - note = "Ranking starts at $need projects — a chart of four is just the list above.", + title = stringResource(Res.string.store_top_charts), + note = stringResource(Res.string.store_ghostspec_charts_note, need), glyph = CaSymbols.trendingUp, slotHeight = 46.dp, slotCount = 3, ) "collections" -> GhostSpec( - title = "Collections", - note = "The team builds a collection once there are enough projects to theme one.", + title = stringResource(Res.string.store_collections_title), + note = stringResource(Res.string.store_ghostspec_collections_note), glyph = CaSymbols.rocketLaunch, slotHeight = 72.dp, slotCount = 2, ) else -> GhostSpec( - title = "Because you installed…", - note = "Recommendations need install history across several projects.", + title = stringResource(Res.string.store_ghostspec_rec_title), + note = stringResource(Res.string.store_ghostspec_rec_note), glyph = CaSymbols.hub, slotHeight = 60.dp, slotCount = 3, @@ -568,22 +584,22 @@ private fun ExploreEmpty( modifier = Modifier.padding(top = 4.dp), ) } - item("steps_head") { SectionHeader("How publishing works") } - item("steps") { PublishingSteps(defaultPublishSteps) } + item("steps_head") { SectionHeader(stringResource(Res.string.store_how_publishing_title)) } + item("steps") { PublishingSteps(defaultPublishSteps()) } } if (bundled.isNotEmpty()) { item("bundled_head") { SectionHeader( - "Bundled with your IDE", - subtitle = "Offline scaffolds that ship with the install.", + stringResource(Res.string.store_bundled_title), + subtitle = stringResource(Res.string.store_bundled_subtitle), ) } itemsIndexed(bundled, key = { _, it -> "bundled_${it.id}" }) { i, item -> Spacer(Modifier.height(10.dp)) BundledTemplateRow( title = item.title, - meta = listOfNotNull(item.language, "offline").joinToString(" · "), + meta = listOfNotNull(item.language, stringResource(Res.string.store_meta_offline)).joinToString(" · "), iconId = item.iconId, index = i, onUse = { onUseBundled(item) }, @@ -591,7 +607,7 @@ private fun ExploreEmpty( } } - item("ghosts_head") { SectionHeader("What lands here next") } + item("ghosts_head") { SectionHeader(stringResource(Res.string.store_ghost_next_title)) } // No progress counters here: at zero there is nothing to be partway toward, so each note // states the CONDITION that fills the shelf instead. itemsIndexed(EMPTY_GHOSTS, key = { _, k -> "ghost_$k" }) { _, key -> diff --git a/ide-ui/src/commonMain/kotlin/dev/ide/ui/screens/UiActionLocalization.kt b/ide-ui/src/commonMain/kotlin/dev/ide/ui/screens/UiActionLocalization.kt new file mode 100644 index 0000000000..a7aa21bba3 --- /dev/null +++ b/ide-ui/src/commonMain/kotlin/dev/ide/ui/screens/UiActionLocalization.kt @@ -0,0 +1,54 @@ +package dev.ide.ui.screens + +import androidx.compose.runtime.Composable +import dev.ide.ui.ext.UiHostAction +import dev.ide.ui.generated.resources.* +import org.jetbrains.compose.resources.StringResource +import org.jetbrains.compose.resources.stringResource + +/** + * A localization overlay for the built-in UI actions (the "More" menu rows and the command palette's + * UI-navigation commands). Their titles/descriptions are declared in `ide-ui-api` + * ([dev.ide.ui.ext.BuiltInUiPlugin]) as plain English — the plugin API carries plain strings (third-party + * plugins have no Compose-resource access), so rather than translate there, the UI maps a built-in action id + * to a string resource here and resolves it at render time. + * + * Same OVERRIDE pattern as [SettingsLocalization]: where a resource exists it wins, otherwise the + * plugin-supplied string is used verbatim, keeping third-party plugin actions working (unknown ids fall + * through). + * + * Keys mirror the [dev.ide.ui.ext.SimpleUiAction] ids. + */ + +// Action title, by action id. +private val ACTION_TITLE: Map = mapOf( + "ui.hub" to Res.string.action_hub, + "ui.modules" to Res.string.action_modules, + "ui.icons" to Res.string.action_icons, + "ui.dependencies" to Res.string.action_dependencies, + "ui.reindex" to Res.string.action_reindex, + "ui.logs" to Res.string.action_logs, + "ui.toggleTheme" to Res.string.action_toggle_theme, + "ui.closeProject" to Res.string.action_close_project, +) + +// Action description, by action id (only the built-ins that carry one). +private val ACTION_DESC: Map = mapOf( + "ui.hub" to Res.string.action_hub_desc, + "ui.modules" to Res.string.action_modules_desc, + "ui.icons" to Res.string.action_icons_desc, + "ui.reindex" to Res.string.action_reindex_desc, + "ui.logs" to Res.string.action_logs_desc, + "ui.toggleTheme" to Res.string.action_toggle_theme_desc, + "ui.closeProject" to Res.string.action_close_project_desc, +) + +/** The localized title for a built-in UI action; the plugin-supplied title for a third-party action. */ +@Composable +fun localizedUiActionText(action: UiHostAction): String = + ACTION_TITLE[action.id]?.let { stringResource(it) } ?: action.text + +/** The localized description for a built-in UI action, falling back to the plugin-supplied one (may be null). */ +@Composable +fun localizedUiActionDescription(action: UiHostAction): String? = + ACTION_DESC[action.id]?.let { stringResource(it) } ?: action.description diff --git a/vcs-ui/src/commonMain/composeResources/values-zh/strings.xml b/vcs-ui/src/commonMain/composeResources/values-zh/strings.xml new file mode 100644 index 0000000000..a3326e66f9 --- /dev/null +++ b/vcs-ui/src/commonMain/composeResources/values-zh/strings.xml @@ -0,0 +1,211 @@ + + + + Git + 刷新 + 取消 + 复制 + 已复制 + 在浏览器中打开 + + + 未纳入版本控制 + 使用 Git 跟踪此项目,即可保留更改历史、在分支上工作并推送到 GitHub。 + 创建仓库 + 克隆仓库 + 打开一个项目即可使用版本控制。 + 没有可提交的内容 + 你的工作区与最近一次提交一致。 + + + 分支 + 游离 HEAD + 尚无提交 + 已是最新 + 抓取 + 拉取 + 推送 + 历史 + 此文件的历史 + + + 冲突 + 已暂存 + 更改 + 暂存 + 取消暂存 + 全部暂存 + 全部取消暂存 + 丢弃 + 丢弃更改? + 对 %1$s 的修改将被丢弃。此操作无法撤销。 + 标记为已解决 + 中止合并 + 合并进行中 + 变基进行中 + Cherry-pick 进行中 + 回退进行中 + 二分查找进行中 + + + 已添加 + 已修改 + 已删除 + 已重命名 + 已复制 + 新增 + 冲突 + + + 提交信息 + 提交 + 提交并推送 + 修正上次提交 + 先暂存文件才能提交。 + 提交前请先设置姓名和邮箱。 + 设置身份信息 + 姓名 + 邮箱 + 保存 + + + 分支 + 搜索分支 + 本地 + 远程 + 没有匹配的分支。 + 新建分支 + 分支名称 + 创建 + 当前 + 合并到 %1$s + 删除 + 删除 %1$s? + 该分支将从本地移除。仅存在于该分支上的提交可能会丢失。 + 仍要删除 + + + 尚无提交。 + 加载更多 + %1$d 个文件更改 + +%1$d + -%1$d + 合并 + + + 差异 + 没有文本差异。 + 二进制文件,没有可显示的文本差异。 + + + 贮藏 + 贮藏列表 + 贮藏更改 + 描述(可选) + 包含未跟踪的文件 + 应用 + 丢弃 + 没有贮藏的内容。 + + + 添加默认 .gitignore 条目 + + + 账号 + 使用 GitHub 登录 + 登录后可克隆你的私有仓库、通过 HTTPS 免密码推送,以及创建拉取请求。 + 退出登录 + 用于 Git 操作 + 使用此账号 + 在 GitHub 上输入此代码 + 打开 %1$s 并输入代码。你批准后此页面会立即更新。 + 等待批准… + 使用访问令牌 + 个人访问令牌 + 在 GitHub 的 Settings、Developer settings、Personal access tokens 中创建一个带有 "repo" 权限范围的令牌。 + 登录 + 其他 Git 服务器 + 为不支持账号登录的服务器保存用户名和密码,例如自托管的 GitLab 或 Gitea。 + 主机 + git.example.com + 用户名 + 密码或令牌 + 已保存的服务器 + 移除 + + + 克隆仓库 + 仓库 URL + 文件夹名称 + 克隆 + 你的仓库 + 搜索仓库 + 登录后即可浏览你的仓库。 + 未找到仓库。 + 无法识别的项目 + %1$s 已克隆并添加到你的项目中,但没有构建系统能识别它。你可以浏览和编辑其文件;构建和运行需要先设置好模块。 + 以编辑方式打开 + 私有 + Fork + + + GitHub + 发布到 GitHub + 为此项目创建一个仓库,并将当前分支推送到该仓库。 + 仓库名称 + 描述(可选) + 私有仓库 + 发布 + 发布前请先进行一次提交。 + 拉取请求 + 没有开启中的拉取请求。 + 新建拉取请求 + 标题 + 描述 + 合并到 + 创建拉取请求 + 来自 %1$s + 草稿 + + + 远程仓库 + 添加远程仓库 + 远程仓库名称 + 远程仓库 URL + 此项目还没有远程仓库。 + + + 双方都修改了这些文件。逐个修复后,再将其标记为已解决。 + 已就绪待提交。这些文件会进入你的下一次提交。 + 已修改但尚未纳入。暂存文件即可将其加入下一次提交。 + 提交会将已暂存的文件保存为快照,仅保存在本设备上。 + 推送会将你的提交发送到远程仓库,让其他人可以看到。 + + 更多操作 + 仓库 + 此文件 + 查看更改 + 文件历史 + 同步 + 重新读取文件状态 + 浏览历史提交 + 检查远程是否有新提交,不改动你的文件 + 将远程的提交拉入你的分支 + 将你的提交发送到远程仓库 + 你的 GitHub 账号、发布和拉取请求 + 将此文件加入下一次提交 + 将此文件移出下一次提交 + 将所有已更改的文件加入下一次提交 + 将所有文件移出下一次提交 + 丢弃你对这个文件的修改 + 按当前内容接受此文件 + %1$s。点按可切换或新建分支。 + 提交后推送 + 未设置远程仓库 + 连接到 GitHub + 退出 %1$s + 切换到此分支 + 删除分支 + 合并到当前分支 +