-
Notifications
You must be signed in to change notification settings - Fork 1.7k
feat: add Simplified Chinese localization #2008
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -347,7 +347,7 @@ test("clicking X in the panel closes the panel and the camera preview", async () | |
| const panelFrame = frameWithUrl(targetPage, "popup.html"); | ||
| if (!panelFrame) throw new Error("panel frame missing"); | ||
| await panelFrame | ||
| .locator('button[aria-label="Close Cap and hide all recorder UI"]') | ||
| .locator('button[aria-label="关闭 Cap 并隐藏所有录制界面"]') | ||
| .click(); | ||
|
|
||
| // The panel and the camera preview should both tear down. | ||
|
|
@@ -419,7 +419,7 @@ test("a failed recording start reopens the panel with the error", async () => { | |
| .toBe(true); | ||
| const panelFrame = frameWithUrl(targetPage, "popup.html"); | ||
| if (!panelFrame) throw new Error("panel frame missing"); | ||
| await expect(panelFrame.getByText("Recording failed.")).toBeVisible({ | ||
| await expect(panelFrame.getByText("录制失败。")).toBeVisible({ | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same idea here: asserting exact localized sentences ( |
||
| timeout: 10_000, | ||
| }); | ||
| await targetPage.screenshot({ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -146,9 +146,7 @@ const createTab = (url: string) => | |
| new Promise<chrome.tabs.Tab>((resolve, reject) => { | ||
| chrome.tabs.create({ url, active: true }, (tab) => { | ||
| if (chrome.runtime.lastError) { | ||
| reject( | ||
| new Error(chrome.runtime.lastError.message ?? "Failed to open tab"), | ||
| ); | ||
| reject(new Error(chrome.runtime.lastError.message ?? "打开标签页失败")); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These fallback error strings are now Chinese. If the extension isn’t actually locale-switched yet, this will surface Chinese for everyone and makes logs/debugging harder to search. Consider sourcing user-facing copy via |
||
| return; | ||
| } | ||
| resolve(tab); | ||
|
|
@@ -160,9 +158,7 @@ const updateTab = (tabId: number, url: string) => | |
| chrome.tabs.update(tabId, { url, active: true }, (tab) => { | ||
| if (chrome.runtime.lastError || !tab) { | ||
| reject( | ||
| new Error( | ||
| chrome.runtime.lastError?.message ?? "Failed to update tab", | ||
| ), | ||
| new Error(chrome.runtime.lastError?.message ?? "更新标签页失败"), | ||
| ); | ||
| return; | ||
| } | ||
|
|
@@ -219,7 +215,7 @@ const createOffscreenDocument = () => | |
| { | ||
| url: OFFSCREEN_URL, | ||
| reasons: ["USER_MEDIA", "DISPLAY_MEDIA", "BLOBS", "AUDIO_PLAYBACK"], | ||
| justification: "Record and upload Cap videos from an extension page.", | ||
| justification: "从扩展页面录制并上传 Cap 视频。", | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| }, | ||
| () => { | ||
| const error = chrome.runtime.lastError; | ||
|
|
@@ -228,7 +224,7 @@ const createOffscreenDocument = () => | |
| return; | ||
| } | ||
|
|
||
| const message = error.message ?? "Failed to create offscreen document"; | ||
| const message = error.message ?? "创建后台录制文档失败"; | ||
| if (message.toLowerCase().includes("single offscreen document")) { | ||
| resolve(); | ||
| return; | ||
|
|
@@ -267,7 +263,7 @@ const sendOffscreenRuntimeMessage = (message: OffscreenRequest) => | |
| new Promise<OffscreenResponse>((resolve, reject) => { | ||
| chrome.runtime.sendMessage(message, (response) => { | ||
| if (chrome.runtime.lastError) { | ||
| reject(new Error(chrome.runtime.lastError.message ?? "Message failed")); | ||
| reject(new Error(chrome.runtime.lastError.message ?? "消息发送失败")); | ||
| return; | ||
| } | ||
| resolve(response as OffscreenResponse); | ||
|
|
@@ -312,11 +308,7 @@ const getTabStreamId = (tabId: number) => | |
| new Promise<string>((resolve, reject) => { | ||
| chrome.tabCapture.getMediaStreamId({ targetTabId: tabId }, (streamId) => { | ||
| if (chrome.runtime.lastError) { | ||
| reject( | ||
| new Error( | ||
| chrome.runtime.lastError.message ?? "Failed to capture tab", | ||
| ), | ||
| ); | ||
| reject(new Error(chrome.runtime.lastError.message ?? "录制标签页失败")); | ||
| return; | ||
| } | ||
| resolve(streamId); | ||
|
|
@@ -596,9 +588,7 @@ const updateActionForStatus = (nextStatus: RecordingStatus) => { | |
| // The recorder renders inside the page, so the action never opens a popup. | ||
| setActionPopup(""), | ||
| setActionBadgeText(isCapturing ? "REC" : ""), | ||
| setActionTitle( | ||
| isCapturing ? "Stop Cap recording" : "Record your screen with Cap", | ||
| ), | ||
| setActionTitle(isCapturing ? "停止 Cap 录制" : "使用 Cap 录制屏幕"), | ||
| ]).then(() => undefined); | ||
| }; | ||
|
|
||
|
|
@@ -849,7 +839,7 @@ const waitForWebcamPreviewReady = (tabId: number) => { | |
| previewReadyWaiters, | ||
| tabId, | ||
| START_PREVIEW_READY_TIMEOUT_MS, | ||
| "Camera preview did not become ready before recording started.", | ||
| "开始录制前摄像头预览未准备就绪。", | ||
| ); | ||
| }; | ||
|
|
||
|
|
@@ -1133,7 +1123,7 @@ const loadSignedInState = async () => { | |
| const requireSignedInState = async () => { | ||
| const state = await loadSignedInState(); | ||
| if (!state.auth || !state.bootstrap) { | ||
| throw new Error("Sign in to Cap first"); | ||
| throw new Error("请先登录 Cap"); | ||
| } | ||
| return state as { | ||
| settings: ExtensionSettings; | ||
|
|
@@ -1230,7 +1220,7 @@ const startRecording = async (mode: RecordingMode) => { | |
| await saveSettings(recordingSettings); | ||
| } | ||
| if (mode === "camera" && !recordingSettings.webcam.deviceId) { | ||
| throw new Error("Select a camera before recording."); | ||
| throw new Error("请先选择摄像头再开始录制。"); | ||
| } | ||
| if (isWebcamPreviewEnabled(recordingSettings)) { | ||
| await saveWebcamPreviewDismissed(false); | ||
|
|
@@ -1249,7 +1239,7 @@ const startRecording = async (mode: RecordingMode) => { | |
| return { | ||
| ok: false, | ||
| canceled: true, | ||
| error: "Recording canceled", | ||
| error: "录制已取消", | ||
| } satisfies OffscreenResponse; | ||
| } | ||
| } | ||
|
|
@@ -1396,10 +1386,7 @@ const launchWebAuthFlow = (url: string) => | |
| (responseUrl) => { | ||
| if (chrome.runtime.lastError || !responseUrl) { | ||
| reject( | ||
| new Error( | ||
| chrome.runtime.lastError?.message ?? | ||
| "The sign-in window was closed", | ||
| ), | ||
| new Error(chrome.runtime.lastError?.message ?? "登录窗口已关闭"), | ||
| ); | ||
| return; | ||
| } | ||
|
|
@@ -1492,7 +1479,7 @@ const handlePreviewError = async ( | |
| settleTabWaiters( | ||
| previewReadyWaiters, | ||
| tabId, | ||
| new Error("Camera preview did not become ready."), | ||
| new Error("摄像头预览未准备就绪。"), | ||
| ); | ||
|
|
||
| const fallbackTabId = | ||
|
|
@@ -1552,7 +1539,7 @@ const handleRequest = async ( | |
| const status = error instanceof ApiRequestError ? error.status : null; | ||
| if (status === null || status >= 500) { | ||
| throw new Error( | ||
| "Could not reach Cap to revoke this sign-in. Check your connection and try again.", | ||
| "无法连接 Cap 以撤销本次登录,请检查网络连接后重试。", | ||
| ); | ||
| } | ||
| } | ||
|
|
@@ -1741,7 +1728,7 @@ const handleRequest = async ( | |
| // excluded so a web-accessible page embedded by a hostile site cannot | ||
| // authorise itself. | ||
| if (!isWebPageSender(sender)) { | ||
| return { ok: false, error: "Unauthorized" }; | ||
| return { ok: false, error: "未授权" }; | ||
| } | ||
| await registerOverlayToken(message.token); | ||
| return { ok: true }; | ||
|
|
@@ -1757,7 +1744,7 @@ const handleRequest = async ( | |
| message.sessionId, | ||
| ); | ||
| if (!allowed) { | ||
| return { ok: false, error: "Camera preview is not authorized." }; | ||
| return { ok: false, error: "摄像头预览未获授权。" }; | ||
| } | ||
| const response = await sendOffscreen({ | ||
| target: "offscreen", | ||
|
|
@@ -1789,7 +1776,7 @@ const handleRequest = async ( | |
| tabId === undefined || | ||
| !(await isCameraPreviewEventAllowed(sender, message.token)) | ||
| ) { | ||
| return { ok: false, error: "Unauthorized" }; | ||
| return { ok: false, error: "未授权" }; | ||
| } | ||
| chrome.tabs.sendMessage( | ||
| tabId, | ||
|
|
@@ -1840,7 +1827,7 @@ const handleRequest = async ( | |
| return { ok: true }; | ||
| } | ||
|
|
||
| return { ok: false, error: "Unknown request" }; | ||
| return { ok: false, error: "未知请求" }; | ||
| }; | ||
|
|
||
| chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These e2e selectors are now coupled to translated
aria-labelcopy. That tends to be brittle as soon as copy tweaks happen (or more locales get added). Consider adding a non-localized hook (e.g.data-testidor a stableid) to the close button and target that in tests instead.