fix(i18n): translate Git operation titles and their notifications

Git operations run through runOp / runGitOperation took their title, success and failure text
as plain strings, which the text catalog check doesn't see, so the notifications after a commit,
merge, rebase, stash, shelve, tag, patch or rollback ("3 files committed: …", "Merged a into b",
"Checkout x failed") stayed in English, and several spliced plural or English words into the
sentence. The call sites now tag the text with tx, each count and variant has its own sentence,
and a failure is titled "{title} failed" from the translated title.
Цей коміт міститься в:
l0ng-ai committed 2026-11-04 10:56:47 +08:00
1 джерело c86928eee7
коміт f6832a4d2a
15 files changed
+143 -66

No files matched your search

+1 -1
Переглянути файл
@@ -724,7 +724,7 @@ export function DiffEditor({ input, toolbarActions }: { input: EditorInput; tool
logConsole("info", "Nothing to stage for this change");
return;
}
await runOp(`Stage hunk of ${path}`, () => call(commands.vcsStageHunks(path, indices)));
await runOp(tx`Stage hunk of ${path}`, () => call(commands.vcsStageHunks(path, indices)));
await refreshNow();
} finally {
setBusy(false);
+3 -2
Переглянути файл
@@ -97,12 +97,13 @@ export function LocalHistoryEditor({ input }: { input: EditorInput }) {
const ok = await confirmDialog({ title: "Revert", message: question, okLabel: "Revert" });
if (!ok) return;
await runOp(
"Revert from Local History",
tx("Revert from Local History"),
async () => {
for (const e of targets) await call(commands.historyRestore(e.path, e.id));
},
{
success: targets.length === 1 ? `${baseName(targets[0]!.path)} reverted` : `${targets.length} files reverted`,
success:
targets.length === 1 ? tx`${baseName(targets[0]!.path)} reverted` : tx`${targets.length} files reverted`,
refresh: true,
},
);
+2 -2
Переглянути файл
@@ -259,14 +259,14 @@ export function MergeEditor({ input }: { input: EditorInput }) {
/** Saves `text` as the resolved file and stages it; like IDEA, that closes the merge editor. */
const writeResolved = async (text: string | null) => {
const ok = await runOp(
`Resolve ${baseName(path)}`,
tx`Resolve ${baseName(path)}`,
async () => {
if (text === null) await call(commands.fsDelete(path));
else await writeFileText(path, text, { save: true, exact: true });
await call(commands.vcsStage([path]));
return true;
},
{ success: `${baseName(path)}: conflicts resolved` },
{ success: tx`${baseName(path)}: conflicts resolved` },
);
if (ok) {
setResolved(true);
+21 -17
Переглянути файл
@@ -79,7 +79,7 @@ async function performCheckout(
let options: CheckoutOptions = { mode: "normal", diverged: null };
for (;;) {
const opts = options;
const outcome = await runOp(`Checkout ${rev}`, () => call(commands.vcsCheckout(repo, rev, false, opts)));
const outcome = await runOp(tx`Checkout ${rev}`, () => call(commands.vcsCheckout(repo, rev, false, opts)));
if (!outcome) return false;
// `success` may name the branch now checked out.
if (outcome.kind === "done" || outcome.kind === "conflicts") await refreshNow();
@@ -167,7 +167,7 @@ export async function newBranch(from?: string) {
const previous = useVcs.getState().branch?.head;
if (checkout) await saveAll();
const create = () => call(commands.vcsCreateBranch(repo, r.value, from ?? null, checkout));
const ok = await runOp(`Create branch ${r.value}`, create, {
const ok = await runOp(tx`Create branch ${r.value}`, create, {
success: checkout ? `Created and checked out ${r.value}` : `Created branch ${r.value}`,
});
if (ok !== undefined && checkout) {
@@ -208,8 +208,8 @@ export async function openConflictsInstead(): Promise<boolean> {
export async function mergeIntoCurrent(branch: string, repo: string | null = currentRepo()) {
const current = (repo === currentRepo() ? useVcs.getState().branch?.head : null) ?? "HEAD";
await saveAll();
await runOp(`Merge ${branch} into ${current}`, () => call(commands.vcsMerge(repo, branch)), {
success: `Merged ${branch} into ${current}`,
await runOp(tx`Merge ${branch} into ${current}`, () => call(commands.vcsMerge(repo, branch)), {
success: tx`Merged ${branch} into ${current}`,
errorActions: conflictActions,
onError: openConflictsInstead,
});
@@ -219,8 +219,8 @@ export async function rebaseOnto(branch: string) {
const repo = currentRepo();
const current = useVcs.getState().branch?.head ?? "HEAD";
await saveAll();
await runOp(`Rebase ${current} onto ${branch}`, () => call(commands.vcsRebase(repo, branch)), {
success: `Rebased ${current} onto ${branch}`,
await runOp(tx`Rebase ${current} onto ${branch}`, () => call(commands.vcsRebase(repo, branch)), {
success: tx`Rebased ${current} onto ${branch}`,
errorActions: conflictActions,
onError: openConflictsInstead,
});
@@ -238,8 +238,8 @@ export async function renameBranch(name: string) {
});
if (!r) return;
const to = r.value.trim();
const ok = await runOp(`Rename branch ${name}`, () => call(commands.vcsRenameBranch(repo, name, to)), {
success: `Renamed branch ${name} to ${to}`,
const ok = await runOp(tx`Rename branch ${name}`, () => call(commands.vcsRenameBranch(repo, name, to)), {
success: tx`Renamed branch ${name} to ${to}`,
});
if (ok !== undefined) {
try {
@@ -257,7 +257,7 @@ export async function renameBranch(name: string) {
*/
export async function deleteBranch(name: string) {
const repo = currentRepo();
const deleted = await runOp(`Delete branch ${name}`, () => call(commands.vcsDeleteBranch(repo, name)));
const deleted = await runOp(tx`Delete branch ${name}`, () => call(commands.vcsDeleteBranch(repo, name)));
if (!deleted) return;
const tracked = deleted.upstream;
const otherTracking = useVcs.getState().branches.some((b) => !b.remote && b.name !== name && b.upstream === tracked);
@@ -274,8 +274,8 @@ export async function deleteBranch(name: string) {
label: "Restore",
run: () => {
done();
void runOp(`Restore branch ${name}`, () => call(commands.vcsRestoreBranch(repo, deleted)), {
success: `Restored branch ${name}`,
void runOp(tx`Restore branch ${name}`, () => call(commands.vcsRestoreBranch(repo, deleted)), {
success: tx`Restored branch ${name}`,
});
},
},
@@ -379,9 +379,13 @@ export async function newBranchInRepos(roots: readonly string[], from?: string)
await eachRepo(
roots,
async (root) =>
(await runOp(`Create branch ${name}`, () => call(commands.vcsCreateBranch(root, name, from ?? null, r.checked)), {
refresh: false,
})) !== undefined,
(await runOp(
tx`Create branch ${name}`,
() => call(commands.vcsCreateBranch(root, name, from ?? null, r.checked)),
{
refresh: false,
},
)) !== undefined,
r.checked ? `Created and checked out ${name}` : `Created branch ${name}`,
);
}
@@ -392,7 +396,7 @@ export async function mergeInRepos(branch: string, roots: readonly string[]) {
await eachRepo(
roots,
async (root) =>
(await runOp(`Merge ${branch} (${repoName(root)})`, () => call(commands.vcsMerge(root, branch)), {
(await runOp(tx`Merge ${branch} (${repoName(root)})`, () => call(commands.vcsMerge(root, branch)), {
refresh: false,
errorActions: conflictActions,
onError: openConflictsInstead,
@@ -407,7 +411,7 @@ export async function rebaseInRepos(branch: string, roots: readonly string[]) {
await eachRepo(
roots,
async (root) =>
(await runOp(`Rebase onto ${branch} (${repoName(root)})`, () => call(commands.vcsRebase(root, branch)), {
(await runOp(tx`Rebase onto ${branch} (${repoName(root)})`, () => call(commands.vcsRebase(root, branch)), {
refresh: false,
errorActions: conflictActions,
onError: openConflictsInstead,
@@ -428,7 +432,7 @@ export async function deleteInRepos(name: string, roots: readonly string[]) {
await eachRepo(
roots,
async (root) =>
(await runOp(`Delete branch ${name} (${repoName(root)})`, () => call(commands.vcsDeleteBranch(root, name)), {
(await runOp(tx`Delete branch ${name} (${repoName(root)})`, () => call(commands.vcsDeleteBranch(root, name)), {
refresh: false,
})) !== undefined,
`Deleted branch ${name}`,
+27 -12
Переглянути файл
@@ -90,8 +90,23 @@ export async function commitChanges(): Promise<string | undefined> {
success: () => {
const n = plan.included.length;
const summary = firstLine(message);
const where = repos.length > 1 ? ` in ${repos.length} repositories` : "";
return `${n} file${n === 1 ? "" : "s"} ${amend ? "amended" : "committed"}${where}${summary ? `: ${summary}` : ""}`;
const several = repos.length;
const done = amend
? several > 1
? n === 1
? tx`1 file amended in ${several} repositories`
: tx`${n} files amended in ${several} repositories`
: n === 1
? tx("1 file amended")
: tx`${n} files amended`
: several > 1
? n === 1
? tx`1 file committed in ${several} repositories`
: tx`${n} files committed in ${several} repositories`
: n === 1
? tx("1 file committed")
: tx`${n} files committed`;
return summary ? `${done}: ${summary}` : done;
},
},
);
@@ -199,12 +214,12 @@ function RollbackDialog({ files, onClose }: { files: FileStatus[]; onClose: () =
await saveAll();
const { unstage, discard } = rollbackPlan(chosen, deleteAdded);
await runOp(
"Rollback",
tx("Rollback"),
async () => {
if (unstage.length) await call(commands.vcsUnstage(unstage));
if (discard.length) await call(commands.vcsDiscard(discard));
},
{ success: `Rolled back ${chosen.length} file${chosen.length === 1 ? "" : "s"}` },
{ success: chosen.length === 1 ? tx("Rolled back 1 file") : tx`Rolled back ${chosen.length} files` },
);
};
return (
@@ -284,7 +299,7 @@ export async function addToVcs(paths: readonly string[]) {
});
return;
}
await runOp(`Add ${targets.length} file${targets.length === 1 ? "" : "s"} to Git`, () =>
await runOp(targets.length === 1 ? tx("Add 1 file to Git") : tx`Add ${targets.length} files to Git`, () =>
call(commands.vcsStage(targets.map(stripSlash))),
);
}
@@ -299,9 +314,9 @@ export function stashChanges(repo = currentRepo()): Promise<void> {
if (r) {
await saveAll();
await runOp(
"Stash changes",
tx("Stash changes"),
() => call(commands.vcsStashPush(repo, r.message || null, r.includeUntracked, r.keepIndex)),
{ success: "Changes stashed" },
{ success: tx("Changes stashed") },
);
void loadStashes().catch(() => undefined);
}
@@ -372,10 +387,10 @@ export interface UnstashOptions {
export function unstashTitles(o: UnstashOptions): { title: string; success: string } {
const ref = `stash@{${o.index}}`;
if (o.branch)
return { title: tx`Unstash ${ref} as branch ${o.branch}`, success: `Unstashed ${ref} on branch ${o.branch}` };
return { title: tx`Unstash ${ref} as branch ${o.branch}`, success: tx`Unstashed ${ref} on branch ${o.branch}` };
return o.pop
? { title: tx`Pop ${ref}`, success: `Unstashed ${ref}` }
: { title: tx`Apply ${ref}`, success: `Applied ${ref}` };
? { title: tx`Pop ${ref}`, success: tx`Unstashed ${ref}` }
: { title: tx`Apply ${ref}`, success: tx`Applied ${ref}` };
}
export async function unstash(o: UnstashOptions, repo = currentRepo()) {
@@ -482,8 +497,8 @@ export async function dropStash(index: number, message: string, repo = currentRe
danger: true,
});
if (!ok) return;
await runOp(`Drop stash@{${index}}`, () => call(commands.vcsStashDrop(repo, index)), {
success: `Dropped stash@{${index}}`,
await runOp(tx`Drop ${`stash@{${index}}`}`, () => call(commands.vcsStashDrop(repo, index)), {
success: tx`Dropped ${`stash@{${index}}`}`,
});
void loadStashes().catch((e) => logConsole("err", String(e)));
}
+7 -2
Переглянути файл
@@ -4,6 +4,7 @@
* the deletion staged (`git rm`), with the same three choices.
*/
import { onFileOperation, type FileOperation } from "@/platform/fileOps";
import { tx } from "@/platform/i18n";
import { call, commands, isTauri } from "@/platform/ipc";
import { loadSettings } from "@/platform/project";
import { workflowSettings } from "@/platform/workflowSettings";
@@ -80,8 +81,12 @@ async function handle(op: FileOperation) {
const n = files.length;
await runOp(
op.kind === "created"
? `Add ${n} file${n === 1 ? "" : "s"} to Git`
: `Remove ${n} file${n === 1 ? "" : "s"} from Git`,
? n === 1
? tx("Add 1 file to Git")
: tx`Add ${n} files to Git`
: n === 1
? tx("Remove 1 file from Git")
: tx`Remove ${n} files from Git`,
() => call(commands.vcsStage(files)),
);
}
+2 -2
Переглянути файл
@@ -31,7 +31,7 @@ export function openMergeEditor(path: string) {
async function acceptSide(f: FileStatus, side: "ours" | "theirs") {
const path = stripSlash(f.path);
await runOp(
`Accept ${side === "ours" ? "yours" : "theirs"} for ${baseName(path)}`,
side === "ours" ? tx`Accept yours for ${baseName(path)}` : tx`Accept theirs for ${baseName(path)}`,
async () => {
const v = await call(commands.vcsConflictVersions(path));
const text = side === "ours" ? v.ours : v.theirs;
@@ -43,7 +43,7 @@ async function acceptSide(f: FileStatus, side: "ours" | "theirs") {
}
await call(commands.vcsStage([path]));
},
{ success: `${baseName(path)} resolved` },
{ success: tx`${baseName(path)} resolved` },
);
}
+4 -4
Переглянути файл
@@ -238,9 +238,9 @@ function MergeDialog({ onClose }: { onClose: () => void }) {
await saveAll();
const target = head ?? "HEAD";
await runOp(
`Merge ${branch} into ${target}`,
tx`Merge ${branch} into ${target}`,
() => call(commands.vcsMergeWith(repo, [branch], mergeOptionsOf(flags, withMessage ? message : null))),
{ success: `Merged ${branch} into ${target}`, errorActions: conflictActions, onError: openConflictsInstead },
{ success: tx`Merged ${branch} into ${target}`, errorActions: conflictActions, onError: openConflictsInstead },
);
await refreshNow();
};
@@ -313,7 +313,7 @@ function RebaseDialog({ onClose }: { onClose: () => void }) {
const target = useOnto && onto ? onto : upstream;
const current = head ?? "HEAD";
await runOp(
`Rebase ${current} onto ${target}`,
tx`Rebase ${current} onto ${target}`,
() =>
call(
commands.vcsRebaseWith(repo, upstream, {
@@ -322,7 +322,7 @@ function RebaseDialog({ onClose }: { onClose: () => void }) {
keepEmpty,
}),
),
{ success: `Rebased ${current} onto ${target}`, errorActions: conflictActions, onError: openConflictsInstead },
{ success: tx`Rebased ${current} onto ${target}`, errorActions: conflictActions, onError: openConflictsInstead },
);
await refreshNow();
};
+2 -2
Переглянути файл
@@ -184,7 +184,7 @@ export async function getFromRevision(changes: readonly LogChange[]) {
if (!ok) return;
await saveAll();
await runOp(
"Get from Revision",
tx("Get from Revision"),
async () => {
for (const c of changes) {
const path = absolutePath(c.path, c.repo);
@@ -211,7 +211,7 @@ export async function revertSelectedChanges(changes: readonly LogChange[], commi
const ids = [...commits].reverse().map((c) => c.id);
const paths = new Set(changes.flatMap((c) => (c.oldPath ? [c.path, c.oldPath] : [c.path])));
const outcome = await runOp(
"Revert selected changes",
tx("Revert selected changes"),
async () => {
const patch = filterPatch(
await call(
+10 -10
Переглянути файл
@@ -165,7 +165,7 @@ export async function controlOperation(control: "continue" | "skip" | "abort", e
{
progress,
success,
failure: `${controlLabel(op, control)} failed`,
failure: tx`${controlLabel(op, control)} failed`,
onError: control === "abort" ? undefined : conflictsInstead,
},
);
@@ -241,8 +241,8 @@ export async function cherryPick(commits: Commit[]) {
{ op: "cherryPick", commits: ordered.map((c) => c.id) },
{
progress: "Cherry-picking",
success: `Cherry-picked ${label}`,
failure: "Cherry-pick failed",
success: tx`Cherry-picked ${label}`,
failure: tx("Cherry-pick failed"),
onError: conflictsInstead,
},
commitRepo(commits[0]!) ?? currentRepo(),
@@ -256,7 +256,7 @@ export async function revertCommits(commits: Commit[]) {
ordered.length === 1 ? `${short(ordered[0]!.id)} “${ordered[0]!.summary}”` : `${ordered.length} commits`;
await runGitOperation(
{ op: "revert", commits: ordered.map((c) => c.id) },
{ progress: "Reverting", success: `Reverted ${label}`, failure: "Revert failed", onError: conflictsInstead },
{ progress: "Reverting", success: tx`Reverted ${label}`, failure: tx("Revert failed"), onError: conflictsInstead },
commitRepo(commits[0]!) ?? currentRepo(),
);
}
@@ -294,8 +294,8 @@ function ResetDialog({ commit, onClose }: { commit?: Commit; onClose: () => void
{ op: "reset", commit: rev, mode },
{
progress: "Resetting",
success: `Reset ${head} to ${commit ? short(commit.id) : rev} (${mode})`,
failure: "Reset failed",
success: tx`Reset ${head} to ${commit ? short(commit.id) : rev} (${mode})`,
failure: tx("Reset failed"),
},
repo,
);
@@ -374,8 +374,8 @@ export async function newTagOn(commit: Commit) {
});
if (!r) return;
const create = () => call(commands.vcsCreateTag(repo, r.value, commit.id, null, false));
const ok = await runOp(`Create tag ${r.value}`, create, {
success: `Created tag ${r.value} on ${short(commit.id)}`,
const ok = await runOp(tx`Create tag ${r.value}`, create, {
success: tx`Created tag ${r.value} on ${short(commit.id)}`,
});
// Tags are not part of the refs signature that reloads the log.
if (ok !== undefined) void reloadLog();
@@ -402,9 +402,9 @@ function TagDialog({ onClose }: { onClose: () => void }) {
onClose();
const tag = name.trim();
const ok = await runOp(
`Create tag ${tag}`,
tx`Create tag ${tag}`,
() => call(commands.vcsCreateTag(repo, tag, commit.trim() || "HEAD", message || null, force)),
{ success: `Created tag ${tag}` },
{ success: tx`Created tag ${tag}` },
);
if (ok !== undefined && useGitLog.getState().commits.length) void reloadLog();
};
+1 -1
Переглянути файл
@@ -89,7 +89,7 @@ export async function runOp<T>(title: string, fn: () => Promise<T>, opts: OpOpti
notifications().notify({
group: "vcs",
level: "error",
title: `${title} failed`,
title: tx`${title} failed`,
body: message,
actions: opts.errorActions?.(message),
});
+2 -2
Переглянути файл
@@ -116,7 +116,7 @@ function CreatePatchDialog({ origin, onClose }: { origin: PatchOrigin; onClose:
reverse,
};
const done = await runOp(
"Create patch",
tx("Create patch"),
async () => {
const patch =
origin.kind === "shelf"
@@ -250,7 +250,7 @@ function ApplyPatchDialog({ patch, source, onClose }: { patch: string; source: s
const run = async () => {
onClose();
await saveAll();
const outcome = await runOp(`Apply patch (${source})`, () => call(commands.vcsApplyPatch(repo, patch)), {
const outcome = await runOp(tx`Apply patch (${source})`, () => call(commands.vcsApplyPatch(repo, patch)), {
refresh: false,
});
if (outcome) await reportApply(outcome, "Patch applied");
+3 -3
Переглянути файл
@@ -62,8 +62,8 @@ export async function undoCommit(commit: Commit) {
if (!ok) return;
}
const undo = () => call(commands.vcsUndoCommit(repo, commit.id));
const message = await runOp(`Undo commit ${short(commit.id)}`, undo, {
success: `Undid commit ${short(commit.id)} “${commit.summary}”; its changes are staged`,
const message = await runOp(tx`Undo commit ${short(commit.id)}`, undo, {
success: tx`Undid commit ${short(commit.id)} “${commit.summary}”; its changes are staged`,
});
// IDEA offers the undone message for the next commit.
if (message !== undefined && !useCommit.getState().message.trim()) useCommit.setState({ message });
@@ -123,7 +123,7 @@ async function runRebase(base: string | null, entries: RebaseEntry[], done: stri
{
progress: "Rebasing",
success: () => (inProgress() === "rebase" ? "Rebase stopped for editing; continue the rebase when done" : done),
failure: "Rebase failed",
failure: tx("Rebase failed"),
onError: conflictsInstead,
},
repo,
+5 -5
Переглянути файл
@@ -144,7 +144,7 @@ function ShelveDialog({
if (!chosen.length || !name.trim()) return;
onClose();
await saveAll();
const entry = await runOp(`Shelve changes “${name.trim()}”`, () => call(commands.vcsShelve(name.trim(), chosen)));
const entry = await runOp(tx`Shelve changes “${name.trim()}”`, () => call(commands.vcsShelve(name.trim(), chosen)));
if (!entry) return;
void loadShelves().catch(() => undefined);
notifications().notify({
@@ -218,7 +218,7 @@ function UnshelveDialog({ entry, path, onClose }: { entry: ShelvedChangeList; pa
onClose();
await saveAll();
const outcome = await runOp(
`Unshelve “${entry.name}”`,
tx`Unshelve “${entry.name}”`,
() => call(commands.vcsUnshelve(entry.id, remove, path ?? null)),
{
refresh: false,
@@ -272,7 +272,7 @@ export async function deleteShelf(entry: ShelvedChangeList, path?: string) {
danger: true,
});
if (!ok) return;
await runOp(`Delete shelved changes “${entry.name}”`, () => call(commands.vcsShelfDelete(entry.id, path ?? null)), {
await runOp(tx`Delete shelved changes “${entry.name}”`, () => call(commands.vcsShelfDelete(entry.id, path ?? null)), {
refresh: false,
});
void loadShelves().catch(() => undefined);
@@ -286,7 +286,7 @@ export async function renameShelf(entry: ShelvedChangeList) {
okLabel: "Rename",
});
if (!r || r.value === entry.name) return;
await runOp("Rename shelved changes", () => call(commands.vcsShelfRename(entry.id, r.value)), { refresh: false });
await runOp(tx("Rename shelved changes"), () => call(commands.vcsShelfRename(entry.id, r.value)), { refresh: false });
void loadShelves().catch(() => undefined);
}
@@ -388,7 +388,7 @@ export async function importShelfPatches(paths?: string[]) {
if (!picked) return;
for (const path of typeof picked === "string" ? [picked] : picked) {
await runOp(
"Import patches",
tx("Import patches"),
async () => call(commands.vcsShelfImport(repo, baseName(path), await call(commands.fsReadText(path)))),
{ refresh: false },
);
+53 -1
Переглянути файл
@@ -5858,5 +5858,57 @@
"Could not start Compose project {project}": "无法启动 Compose 项目 {project}",
"Could not stop Compose project {project}": "无法停止 Compose 项目 {project}",
"Could not restart Compose project {project}": "无法重启 Compose 项目 {project}",
"Could not take down Compose project {project}": "无法停止并移除 Compose 项目 {project}"
"Could not take down Compose project {project}": "无法停止并移除 Compose 项目 {project}",
"Stage hunk of {path}": "暂存 {path} 的块",
"{path} reverted": "已还原 {path}",
"{length} files reverted": "已还原 {length} 个文件",
"Resolve {path}": "解决 {path}",
"{path}: conflicts resolved": "{path}:冲突已解决",
"Create branch {value}": "创建分支 {value}",
"Merge {branch} into {current}": "将 {branch} 合并到 {current}",
"Merged {branch} into {current}": "已将 {branch} 合并到 {current}",
"Rebase {current} onto {branch}": "将 {current} 变基到 {branch}",
"Rebased {current} onto {branch}": "已将 {current} 变基到 {branch}",
"Rename branch {name}": "重命名分支 {name}",
"Renamed branch {name} to {to}": "已将分支 {name} 重命名为 {to}",
"Delete branch {name}": "删除分支 {name}",
"Restore branch {name}": "恢复分支 {name}",
"Merge {branch} ({root})": "合并 {branch}({root})",
"Rebase onto {branch} ({root})": "变基到 {branch}({root})",
"Delete branch {name} ({root})": "删除分支 {name}({root})",
"1 file amended in {several} repositories": "已在 {several} 个仓库中修改 1 个文件的提交",
"{n} files amended in {several} repositories": "已在 {several} 个仓库中修改 {n} 个文件的提交",
"1 file amended": "已修改 1 个文件的提交",
"{n} files amended": "已修改 {n} 个文件的提交",
"1 file committed in {several} repositories": "已在 {several} 个仓库中提交 1 个文件",
"{n} files committed in {several} repositories": "已在 {several} 个仓库中提交 {n} 个文件",
"1 file committed": "已提交 1 个文件",
"{n} files committed": "已提交 {n} 个文件",
"Rolled back 1 file": "已回滚 1 个文件",
"Rolled back {length} files": "已回滚 {length} 个文件",
"Add 1 file to Git": "将 1 个文件添加到 Git",
"Add {length} files to Git": "将 {length} 个文件添加到 Git",
"Stash changes": "储藏更改",
"Changes stashed": "已储藏更改",
"Unstashed {ref} on branch {branch}": "已在分支 {branch} 上取消储藏 {ref}",
"Unstashed {ref}": "已取消储藏 {ref}",
"Applied {ref}": "已应用 {ref}",
"Dropped {ref}": "已删除 {ref}",
"Remove 1 file from Git": "从 Git 中移除 1 个文件",
"Remove {n} files from Git": "从 Git 中移除 {n} 个文件",
"Accept yours for {path}": "对 {path} 接受你的版本",
"Accept theirs for {path}": "对 {path} 接受他们的版本",
"{path} resolved": "{path} 已解决",
"Revert selected changes": "还原所选更改",
"Create tag {value}": "创建标签 {value}",
"Created tag {value} on {id}": "已在 {id} 上创建标签 {value}",
"Created tag {tag}": "已创建标签 {tag}",
"Create patch": "创建补丁",
"Apply patch ({source})": "应用补丁({source})",
"Undo commit {id}": "撤销提交 {id}",
"Undid commit {id} “{summary}”; its changes are staged": "已撤销提交 {id}“{summary}”,其更改已暂存",
"Shelve changes “{trim}”": "搁置更改“{trim}”",
"Unshelve “{name}”": "取消搁置“{name}”",
"Delete shelved changes “{name}”": "删除搁置的更改“{name}”",
"Rename shelved changes": "重命名搁置的更改"
}