fix(windows): normalize Windows file paths with leading slash and add i18n to link safety dialog
Strip spurious leading slash from Windows drive-letter paths (/D:/foo → D:/foo) in parseLocalFileTarget so that workspace-relative comparison succeeds and local files can be opened correctly. Display the normalized path in the confirmation dialog. Internationalize all link safety dialog and toast strings across 10 locales.
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useCallback, useMemo, useState } from "react"
|
import { useCallback, useMemo, useState } from "react"
|
||||||
|
import { useTranslations } from "next-intl"
|
||||||
import { openUrl } from "@/lib/platform"
|
import { openUrl } from "@/lib/platform"
|
||||||
import type { LinkSafetyConfig, LinkSafetyModalProps } from "streamdown"
|
import type { LinkSafetyConfig, LinkSafetyModalProps } from "streamdown"
|
||||||
import { toast } from "sonner"
|
import { toast } from "sonner"
|
||||||
@@ -29,6 +30,14 @@ function normalizeSlashPath(path: string): string {
|
|||||||
return path.replace(/\\/g, "/")
|
return path.replace(/\\/g, "/")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Strip leading slash before Windows drive letter: /C:/foo → C:/foo */
|
||||||
|
function stripLeadingSlashOnWindows(p: string): string {
|
||||||
|
if (p.startsWith("/") && WINDOWS_ABSOLUTE_PATH.test(p.slice(1))) {
|
||||||
|
return p.slice(1)
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
function decodeUriSafely(value: string): string {
|
function decodeUriSafely(value: string): string {
|
||||||
try {
|
try {
|
||||||
return decodeURIComponent(value)
|
return decodeURIComponent(value)
|
||||||
@@ -92,10 +101,7 @@ function parseLocalFileTarget(rawUrl: string): LocalFileTarget | null {
|
|||||||
try {
|
try {
|
||||||
const parsed = new URL(raw)
|
const parsed = new URL(raw)
|
||||||
const rawPathname = decodeUriSafely(parsed.pathname)
|
const rawPathname = decodeUriSafely(parsed.pathname)
|
||||||
const normalizedPathname =
|
const normalizedPathname = stripLeadingSlashOnWindows(rawPathname)
|
||||||
rawPathname.startsWith("/") && WINDOWS_ABSOLUTE_PATH.test(rawPathname)
|
|
||||||
? rawPathname.slice(1)
|
|
||||||
: rawPathname
|
|
||||||
const pathAndLine = splitPathAndLine(normalizedPathname)
|
const pathAndLine = splitPathAndLine(normalizedPathname)
|
||||||
if (!pathAndLine.path) return null
|
if (!pathAndLine.path) return null
|
||||||
return {
|
return {
|
||||||
@@ -118,10 +124,11 @@ function parseLocalFileTarget(rawUrl: string): LocalFileTarget | null {
|
|||||||
const withoutQuery =
|
const withoutQuery =
|
||||||
queryIndex >= 0 ? withoutHash.slice(0, queryIndex) : withoutHash
|
queryIndex >= 0 ? withoutHash.slice(0, queryIndex) : withoutHash
|
||||||
const pathAndLine = splitPathAndLine(withoutQuery)
|
const pathAndLine = splitPathAndLine(withoutQuery)
|
||||||
if (!isLocalPathLike(pathAndLine.path)) return null
|
const normalizedPath = stripLeadingSlashOnWindows(pathAndLine.path)
|
||||||
|
if (!isLocalPathLike(normalizedPath)) return null
|
||||||
|
|
||||||
return {
|
return {
|
||||||
path: normalizeSlashPath(pathAndLine.path),
|
path: normalizeSlashPath(normalizedPath),
|
||||||
line: parseHashLine(hash) ?? pathAndLine.line,
|
line: parseHashLine(hash) ?? pathAndLine.line,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -163,6 +170,7 @@ function LinkSafetyModal({
|
|||||||
}: LinkSafetyModalProps & {
|
}: LinkSafetyModalProps & {
|
||||||
onAction: (url: string) => Promise<void>
|
onAction: (url: string) => Promise<void>
|
||||||
}) {
|
}) {
|
||||||
|
const t = useTranslations("Folder.chat.linkSafety")
|
||||||
const [opening, setOpening] = useState(false)
|
const [opening, setOpening] = useState(false)
|
||||||
const localTarget = useMemo(() => parseLocalFileTarget(url), [url])
|
const localTarget = useMemo(() => parseLocalFileTarget(url), [url])
|
||||||
const isLocalFile = Boolean(localTarget)
|
const isLocalFile = Boolean(localTarget)
|
||||||
@@ -185,21 +193,27 @@ function LinkSafetyModal({
|
|||||||
<AlertDialogContent>
|
<AlertDialogContent>
|
||||||
<AlertDialogHeader>
|
<AlertDialogHeader>
|
||||||
<AlertDialogTitle>
|
<AlertDialogTitle>
|
||||||
{isLocalFile ? "Open local file?" : "Open external link?"}
|
{isLocalFile ? t("localFileTitle") : t("externalLinkTitle")}
|
||||||
</AlertDialogTitle>
|
</AlertDialogTitle>
|
||||||
<AlertDialogDescription>
|
<AlertDialogDescription>
|
||||||
{isLocalFile
|
{isLocalFile
|
||||||
? "You're about to open a local file in the Files panel."
|
? t("localFileDescription")
|
||||||
: "You're about to visit an external website."}
|
: t("externalLinkDescription")}
|
||||||
</AlertDialogDescription>
|
</AlertDialogDescription>
|
||||||
</AlertDialogHeader>
|
</AlertDialogHeader>
|
||||||
<div className="max-h-28 overflow-auto rounded-md bg-muted px-3 py-2 font-mono text-xs break-all">
|
<div className="max-h-28 overflow-auto rounded-md bg-muted px-3 py-2 font-mono text-xs break-all">
|
||||||
{url}
|
{localTarget?.path ?? url}
|
||||||
</div>
|
</div>
|
||||||
<AlertDialogFooter>
|
<AlertDialogFooter>
|
||||||
<AlertDialogCancel disabled={opening}>Cancel</AlertDialogCancel>
|
<AlertDialogCancel disabled={opening}>
|
||||||
|
{t("cancel")}
|
||||||
|
</AlertDialogCancel>
|
||||||
<AlertDialogAction disabled={opening} onClick={handleAction}>
|
<AlertDialogAction disabled={opening} onClick={handleAction}>
|
||||||
{opening ? "Opening..." : isLocalFile ? "Open file" : "Open link"}
|
{opening
|
||||||
|
? t("opening")
|
||||||
|
: isLocalFile
|
||||||
|
? t("openFile")
|
||||||
|
: t("openLink")}
|
||||||
</AlertDialogAction>
|
</AlertDialogAction>
|
||||||
</AlertDialogFooter>
|
</AlertDialogFooter>
|
||||||
</AlertDialogContent>
|
</AlertDialogContent>
|
||||||
@@ -208,6 +222,7 @@ function LinkSafetyModal({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function useStreamdownLinkSafety(): LinkSafetyConfig {
|
export function useStreamdownLinkSafety(): LinkSafetyConfig {
|
||||||
|
const t = useTranslations("Folder.chat.linkSafety")
|
||||||
const { folder } = useFolderContext()
|
const { folder } = useFolderContext()
|
||||||
const folderPath = folder?.path
|
const folderPath = folder?.path
|
||||||
const { openFilePreview } = useWorkspaceContext()
|
const { openFilePreview } = useWorkspaceContext()
|
||||||
@@ -217,8 +232,8 @@ export function useStreamdownLinkSafety(): LinkSafetyConfig {
|
|||||||
const localTarget = parseLocalFileTarget(url)
|
const localTarget = parseLocalFileTarget(url)
|
||||||
if (localTarget) {
|
if (localTarget) {
|
||||||
if (!folderPath) {
|
if (!folderPath) {
|
||||||
toast.error("Cannot open local file", {
|
toast.error(t("errorCannotOpen"), {
|
||||||
description: "No workspace folder is currently active.",
|
description: t("errorNoWorkspace"),
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -228,8 +243,8 @@ export function useStreamdownLinkSafety(): LinkSafetyConfig {
|
|||||||
folderPath
|
folderPath
|
||||||
)
|
)
|
||||||
if (!relativePath) {
|
if (!relativePath) {
|
||||||
toast.error("Cannot open local file", {
|
toast.error(t("errorCannotOpen"), {
|
||||||
description: "The file is outside the current workspace folder.",
|
description: t("errorOutsideWorkspace"),
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -239,7 +254,7 @@ export function useStreamdownLinkSafety(): LinkSafetyConfig {
|
|||||||
line: localTarget.line ?? undefined,
|
line: localTarget.line ?? undefined,
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error("Failed to open local file", {
|
toast.error(t("errorFailedOpen"), {
|
||||||
description: error instanceof Error ? error.message : String(error),
|
description: error instanceof Error ? error.message : String(error),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -249,12 +264,12 @@ export function useStreamdownLinkSafety(): LinkSafetyConfig {
|
|||||||
try {
|
try {
|
||||||
await openUrl(url)
|
await openUrl(url)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error("Failed to open link", {
|
toast.error(t("errorFailedLink"), {
|
||||||
description: error instanceof Error ? error.message : String(error),
|
description: error instanceof Error ? error.message : String(error),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[folderPath, openFilePreview]
|
[folderPath, openFilePreview, t]
|
||||||
)
|
)
|
||||||
|
|
||||||
const renderModal = useCallback(
|
const renderModal = useCallback(
|
||||||
|
|||||||
@@ -1556,6 +1556,21 @@
|
|||||||
"thoughtForFewSeconds": "تفكير",
|
"thoughtForFewSeconds": "تفكير",
|
||||||
"thoughtForSeconds": "تفكير"
|
"thoughtForSeconds": "تفكير"
|
||||||
},
|
},
|
||||||
|
"linkSafety": {
|
||||||
|
"localFileTitle": "فتح ملف محلي؟",
|
||||||
|
"externalLinkTitle": "فتح رابط خارجي؟",
|
||||||
|
"localFileDescription": "أنت على وشك فتح ملف محلي في لوحة الملفات.",
|
||||||
|
"externalLinkDescription": "أنت على وشك زيارة موقع ويب خارجي.",
|
||||||
|
"cancel": "إلغاء",
|
||||||
|
"opening": "جارٍ الفتح…",
|
||||||
|
"openFile": "فتح الملف",
|
||||||
|
"openLink": "فتح الرابط",
|
||||||
|
"errorCannotOpen": "تعذر فتح الملف المحلي",
|
||||||
|
"errorNoWorkspace": "لا يوجد مجلد مساحة عمل نشط حالياً.",
|
||||||
|
"errorOutsideWorkspace": "الملف خارج مجلد مساحة العمل الحالي.",
|
||||||
|
"errorFailedOpen": "فشل فتح الملف المحلي",
|
||||||
|
"errorFailedLink": "فشل فتح الرابط"
|
||||||
|
},
|
||||||
"messageList": {
|
"messageList": {
|
||||||
"attachedResources": "الموارد المرفقة",
|
"attachedResources": "الموارد المرفقة",
|
||||||
"loading": "جارٍ التحميل...",
|
"loading": "جارٍ التحميل...",
|
||||||
|
|||||||
@@ -1556,6 +1556,21 @@
|
|||||||
"thoughtForFewSeconds": "Nachgedacht",
|
"thoughtForFewSeconds": "Nachgedacht",
|
||||||
"thoughtForSeconds": "Nachgedacht"
|
"thoughtForSeconds": "Nachgedacht"
|
||||||
},
|
},
|
||||||
|
"linkSafety": {
|
||||||
|
"localFileTitle": "Lokale Datei öffnen?",
|
||||||
|
"externalLinkTitle": "Externen Link öffnen?",
|
||||||
|
"localFileDescription": "Sie sind dabei, eine lokale Datei im Dateipanel zu öffnen.",
|
||||||
|
"externalLinkDescription": "Sie sind dabei, eine externe Website zu besuchen.",
|
||||||
|
"cancel": "Abbrechen",
|
||||||
|
"opening": "Wird geöffnet…",
|
||||||
|
"openFile": "Datei öffnen",
|
||||||
|
"openLink": "Link öffnen",
|
||||||
|
"errorCannotOpen": "Lokale Datei kann nicht geöffnet werden",
|
||||||
|
"errorNoWorkspace": "Es ist kein Arbeitsbereichsordner aktiv.",
|
||||||
|
"errorOutsideWorkspace": "Die Datei befindet sich außerhalb des aktuellen Arbeitsbereichsordners.",
|
||||||
|
"errorFailedOpen": "Lokale Datei konnte nicht geöffnet werden",
|
||||||
|
"errorFailedLink": "Link konnte nicht geöffnet werden"
|
||||||
|
},
|
||||||
"messageList": {
|
"messageList": {
|
||||||
"attachedResources": "Angehängte Ressourcen",
|
"attachedResources": "Angehängte Ressourcen",
|
||||||
"loading": "Lädt...",
|
"loading": "Lädt...",
|
||||||
|
|||||||
@@ -1556,6 +1556,21 @@
|
|||||||
"thoughtForFewSeconds": "Thought",
|
"thoughtForFewSeconds": "Thought",
|
||||||
"thoughtForSeconds": "Thought"
|
"thoughtForSeconds": "Thought"
|
||||||
},
|
},
|
||||||
|
"linkSafety": {
|
||||||
|
"localFileTitle": "Open local file?",
|
||||||
|
"externalLinkTitle": "Open external link?",
|
||||||
|
"localFileDescription": "You're about to open a local file in the Files panel.",
|
||||||
|
"externalLinkDescription": "You're about to visit an external website.",
|
||||||
|
"cancel": "Cancel",
|
||||||
|
"opening": "Opening...",
|
||||||
|
"openFile": "Open file",
|
||||||
|
"openLink": "Open link",
|
||||||
|
"errorCannotOpen": "Cannot open local file",
|
||||||
|
"errorNoWorkspace": "No workspace folder is currently active.",
|
||||||
|
"errorOutsideWorkspace": "The file is outside the current workspace folder.",
|
||||||
|
"errorFailedOpen": "Failed to open local file",
|
||||||
|
"errorFailedLink": "Failed to open link"
|
||||||
|
},
|
||||||
"messageList": {
|
"messageList": {
|
||||||
"attachedResources": "Attached resources",
|
"attachedResources": "Attached resources",
|
||||||
"loading": "Loading...",
|
"loading": "Loading...",
|
||||||
|
|||||||
@@ -1556,6 +1556,21 @@
|
|||||||
"thoughtForFewSeconds": "Pensamiento",
|
"thoughtForFewSeconds": "Pensamiento",
|
||||||
"thoughtForSeconds": "Pensamiento"
|
"thoughtForSeconds": "Pensamiento"
|
||||||
},
|
},
|
||||||
|
"linkSafety": {
|
||||||
|
"localFileTitle": "¿Abrir archivo local?",
|
||||||
|
"externalLinkTitle": "¿Abrir enlace externo?",
|
||||||
|
"localFileDescription": "Está a punto de abrir un archivo local en el panel de archivos.",
|
||||||
|
"externalLinkDescription": "Está a punto de visitar un sitio web externo.",
|
||||||
|
"cancel": "Cancelar",
|
||||||
|
"opening": "Abriendo…",
|
||||||
|
"openFile": "Abrir archivo",
|
||||||
|
"openLink": "Abrir enlace",
|
||||||
|
"errorCannotOpen": "No se puede abrir el archivo local",
|
||||||
|
"errorNoWorkspace": "No hay ninguna carpeta de espacio de trabajo activa.",
|
||||||
|
"errorOutsideWorkspace": "El archivo está fuera de la carpeta del espacio de trabajo actual.",
|
||||||
|
"errorFailedOpen": "Error al abrir el archivo local",
|
||||||
|
"errorFailedLink": "Error al abrir el enlace"
|
||||||
|
},
|
||||||
"messageList": {
|
"messageList": {
|
||||||
"attachedResources": "Recursos adjuntos",
|
"attachedResources": "Recursos adjuntos",
|
||||||
"loading": "Cargando...",
|
"loading": "Cargando...",
|
||||||
|
|||||||
@@ -1556,6 +1556,21 @@
|
|||||||
"thoughtForFewSeconds": "Réflexion",
|
"thoughtForFewSeconds": "Réflexion",
|
||||||
"thoughtForSeconds": "Réflexion"
|
"thoughtForSeconds": "Réflexion"
|
||||||
},
|
},
|
||||||
|
"linkSafety": {
|
||||||
|
"localFileTitle": "Ouvrir le fichier local ?",
|
||||||
|
"externalLinkTitle": "Ouvrir le lien externe ?",
|
||||||
|
"localFileDescription": "Vous êtes sur le point d'ouvrir un fichier local dans le panneau de fichiers.",
|
||||||
|
"externalLinkDescription": "Vous êtes sur le point de visiter un site web externe.",
|
||||||
|
"cancel": "Annuler",
|
||||||
|
"opening": "Ouverture…",
|
||||||
|
"openFile": "Ouvrir le fichier",
|
||||||
|
"openLink": "Ouvrir le lien",
|
||||||
|
"errorCannotOpen": "Impossible d'ouvrir le fichier local",
|
||||||
|
"errorNoWorkspace": "Aucun dossier d'espace de travail n'est actuellement actif.",
|
||||||
|
"errorOutsideWorkspace": "Le fichier se trouve en dehors du dossier de l'espace de travail actuel.",
|
||||||
|
"errorFailedOpen": "Échec de l'ouverture du fichier local",
|
||||||
|
"errorFailedLink": "Échec de l'ouverture du lien"
|
||||||
|
},
|
||||||
"messageList": {
|
"messageList": {
|
||||||
"attachedResources": "Ressources jointes",
|
"attachedResources": "Ressources jointes",
|
||||||
"loading": "Chargement...",
|
"loading": "Chargement...",
|
||||||
|
|||||||
@@ -1556,6 +1556,21 @@
|
|||||||
"thoughtForFewSeconds": "考えた",
|
"thoughtForFewSeconds": "考えた",
|
||||||
"thoughtForSeconds": "考えた"
|
"thoughtForSeconds": "考えた"
|
||||||
},
|
},
|
||||||
|
"linkSafety": {
|
||||||
|
"localFileTitle": "ローカルファイルを開きますか?",
|
||||||
|
"externalLinkTitle": "外部リンクを開きますか?",
|
||||||
|
"localFileDescription": "ファイルパネルでローカルファイルを開こうとしています。",
|
||||||
|
"externalLinkDescription": "外部ウェブサイトにアクセスしようとしています。",
|
||||||
|
"cancel": "キャンセル",
|
||||||
|
"opening": "開いています…",
|
||||||
|
"openFile": "ファイルを開く",
|
||||||
|
"openLink": "リンクを開く",
|
||||||
|
"errorCannotOpen": "ローカルファイルを開けません",
|
||||||
|
"errorNoWorkspace": "現在アクティブなワークスペースフォルダがありません。",
|
||||||
|
"errorOutsideWorkspace": "ファイルが現在のワークスペースフォルダの外にあります。",
|
||||||
|
"errorFailedOpen": "ローカルファイルを開けませんでした",
|
||||||
|
"errorFailedLink": "リンクを開けませんでした"
|
||||||
|
},
|
||||||
"messageList": {
|
"messageList": {
|
||||||
"attachedResources": "添付リソース",
|
"attachedResources": "添付リソース",
|
||||||
"loading": "読み込み中...",
|
"loading": "読み込み中...",
|
||||||
|
|||||||
@@ -1556,6 +1556,21 @@
|
|||||||
"thoughtForFewSeconds": "생각함",
|
"thoughtForFewSeconds": "생각함",
|
||||||
"thoughtForSeconds": "생각함"
|
"thoughtForSeconds": "생각함"
|
||||||
},
|
},
|
||||||
|
"linkSafety": {
|
||||||
|
"localFileTitle": "로컬 파일을 여시겠습니까?",
|
||||||
|
"externalLinkTitle": "외부 링크를 여시겠습니까?",
|
||||||
|
"localFileDescription": "파일 패널에서 로컬 파일을 열려고 합니다.",
|
||||||
|
"externalLinkDescription": "외부 웹사이트를 방문하려고 합니다.",
|
||||||
|
"cancel": "취소",
|
||||||
|
"opening": "열고 있습니다…",
|
||||||
|
"openFile": "파일 열기",
|
||||||
|
"openLink": "링크 열기",
|
||||||
|
"errorCannotOpen": "로컬 파일을 열 수 없습니다",
|
||||||
|
"errorNoWorkspace": "현재 활성화된 워크스페이스 폴더가 없습니다.",
|
||||||
|
"errorOutsideWorkspace": "파일이 현재 워크스페이스 폴더 밖에 있습니다.",
|
||||||
|
"errorFailedOpen": "로컬 파일 열기 실패",
|
||||||
|
"errorFailedLink": "링크 열기 실패"
|
||||||
|
},
|
||||||
"messageList": {
|
"messageList": {
|
||||||
"attachedResources": "첨부된 리소스",
|
"attachedResources": "첨부된 리소스",
|
||||||
"loading": "불러오는 중...",
|
"loading": "불러오는 중...",
|
||||||
|
|||||||
@@ -1556,6 +1556,21 @@
|
|||||||
"thoughtForFewSeconds": "Pensamento",
|
"thoughtForFewSeconds": "Pensamento",
|
||||||
"thoughtForSeconds": "Pensamento"
|
"thoughtForSeconds": "Pensamento"
|
||||||
},
|
},
|
||||||
|
"linkSafety": {
|
||||||
|
"localFileTitle": "Abrir arquivo local?",
|
||||||
|
"externalLinkTitle": "Abrir link externo?",
|
||||||
|
"localFileDescription": "Você está prestes a abrir um arquivo local no painel de arquivos.",
|
||||||
|
"externalLinkDescription": "Você está prestes a visitar um site externo.",
|
||||||
|
"cancel": "Cancelar",
|
||||||
|
"opening": "Abrindo…",
|
||||||
|
"openFile": "Abrir arquivo",
|
||||||
|
"openLink": "Abrir link",
|
||||||
|
"errorCannotOpen": "Não é possível abrir o arquivo local",
|
||||||
|
"errorNoWorkspace": "Nenhuma pasta de espaço de trabalho está ativa no momento.",
|
||||||
|
"errorOutsideWorkspace": "O arquivo está fora da pasta do espaço de trabalho atual.",
|
||||||
|
"errorFailedOpen": "Falha ao abrir o arquivo local",
|
||||||
|
"errorFailedLink": "Falha ao abrir o link"
|
||||||
|
},
|
||||||
"messageList": {
|
"messageList": {
|
||||||
"attachedResources": "Recursos anexados",
|
"attachedResources": "Recursos anexados",
|
||||||
"loading": "Carregando...",
|
"loading": "Carregando...",
|
||||||
|
|||||||
@@ -1556,6 +1556,21 @@
|
|||||||
"thoughtForFewSeconds": "思考",
|
"thoughtForFewSeconds": "思考",
|
||||||
"thoughtForSeconds": "思考"
|
"thoughtForSeconds": "思考"
|
||||||
},
|
},
|
||||||
|
"linkSafety": {
|
||||||
|
"localFileTitle": "打开本地文件?",
|
||||||
|
"externalLinkTitle": "打开外部链接?",
|
||||||
|
"localFileDescription": "即将在文件面板中打开一个本地文件。",
|
||||||
|
"externalLinkDescription": "即将访问一个外部网站。",
|
||||||
|
"cancel": "取消",
|
||||||
|
"opening": "正在打开…",
|
||||||
|
"openFile": "打开文件",
|
||||||
|
"openLink": "打开链接",
|
||||||
|
"errorCannotOpen": "无法打开本地文件",
|
||||||
|
"errorNoWorkspace": "当前没有活跃的工作区文件夹。",
|
||||||
|
"errorOutsideWorkspace": "该文件不在当前工作区文件夹内。",
|
||||||
|
"errorFailedOpen": "打开本地文件失败",
|
||||||
|
"errorFailedLink": "打开链接失败"
|
||||||
|
},
|
||||||
"messageList": {
|
"messageList": {
|
||||||
"attachedResources": "附加资源",
|
"attachedResources": "附加资源",
|
||||||
"loading": "加载中...",
|
"loading": "加载中...",
|
||||||
|
|||||||
@@ -1556,6 +1556,21 @@
|
|||||||
"thoughtForFewSeconds": "思考",
|
"thoughtForFewSeconds": "思考",
|
||||||
"thoughtForSeconds": "思考"
|
"thoughtForSeconds": "思考"
|
||||||
},
|
},
|
||||||
|
"linkSafety": {
|
||||||
|
"localFileTitle": "開啟本地檔案?",
|
||||||
|
"externalLinkTitle": "開啟外部連結?",
|
||||||
|
"localFileDescription": "即將在檔案面板中開啟一個本地檔案。",
|
||||||
|
"externalLinkDescription": "即將訪問一個外部網站。",
|
||||||
|
"cancel": "取消",
|
||||||
|
"opening": "正在開啟…",
|
||||||
|
"openFile": "開啟檔案",
|
||||||
|
"openLink": "開啟連結",
|
||||||
|
"errorCannotOpen": "無法開啟本地檔案",
|
||||||
|
"errorNoWorkspace": "目前沒有活躍的工作區資料夾。",
|
||||||
|
"errorOutsideWorkspace": "該檔案不在目前的工作區資料夾內。",
|
||||||
|
"errorFailedOpen": "開啟本地檔案失敗",
|
||||||
|
"errorFailedLink": "開啟連結失敗"
|
||||||
|
},
|
||||||
"messageList": {
|
"messageList": {
|
||||||
"attachedResources": "附加資源",
|
"attachedResources": "附加資源",
|
||||||
"loading": "載入中...",
|
"loading": "載入中...",
|
||||||
|
|||||||
Reference in New Issue
Block a user