fix missing locales
This commit is contained in:
@@ -126,95 +126,99 @@ export function ShortcutsConfigDialog() {
|
||||
if (!open) handleClose();
|
||||
}}
|
||||
>
|
||||
<DialogContent className="bg-[#09090b] border-white/10 text-white max-w-[420px]">
|
||||
<DialogHeader>
|
||||
<DialogContent className="bg-[#09090b] border-white/10 text-white max-w-[420px] max-h-[85vh] flex flex-col">
|
||||
<DialogHeader className="shrink-0">
|
||||
<DialogTitle className="flex items-center gap-2 text-sm">
|
||||
<Keyboard className="w-4 h-4 text-[#34B27B]" />
|
||||
{t("title")}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="space-y-0.5">
|
||||
<p className="text-[10px] text-slate-500 mb-2 uppercase tracking-wide font-semibold">
|
||||
{t("configurable")}
|
||||
</p>
|
||||
{SHORTCUT_ACTIONS.map((action) => {
|
||||
const isCapturing = captureFor === action;
|
||||
const hasConflict = conflict?.forAction === action;
|
||||
return (
|
||||
<div key={action}>
|
||||
<div className="flex items-center justify-between py-1.5 px-1 border-b border-white/5">
|
||||
<span className="text-sm text-slate-300">{t(`actions.${action}`)}</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setConflict(null);
|
||||
setCaptureFor(isCapturing ? null : action);
|
||||
}}
|
||||
title={isCapturing ? t("pressEscToCancel") : t("clickToChange")}
|
||||
className={[
|
||||
"px-2 py-1 rounded text-xs font-mono border transition-all min-w-[90px] text-center select-none",
|
||||
isCapturing
|
||||
? "bg-[#34B27B]/20 border-[#34B27B] text-[#34B27B] animate-pulse"
|
||||
: hasConflict
|
||||
? "bg-amber-500/10 border-amber-500/50 text-amber-400"
|
||||
: "bg-white/5 border-white/10 text-slate-200 hover:border-[#34B27B]/50 hover:text-[#34B27B] cursor-pointer",
|
||||
].join(" ")}
|
||||
>
|
||||
{isCapturing ? t("pressKey") : formatBinding(draft[action], isMac)}
|
||||
</button>
|
||||
</div>
|
||||
{hasConflict && conflict?.conflictWith.type === "configurable" && (
|
||||
<div className="flex items-center justify-between px-1 py-1.5 mb-0.5 bg-amber-500/10 border border-amber-500/20 rounded text-xs">
|
||||
<span className="text-amber-400">
|
||||
⚠{" "}
|
||||
{t("alreadyUsedBy", { action: t(`actions.${conflict.conflictWith.action}`) })}
|
||||
</span>
|
||||
<div className="flex gap-1.5">
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleSwap}
|
||||
className="px-2 py-0.5 bg-amber-500/20 hover:bg-amber-500/30 border border-amber-500/40 rounded text-amber-300 font-medium transition-colors"
|
||||
>
|
||||
{t("swap")}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleCancelConflict}
|
||||
className="px-2 py-0.5 bg-white/5 hover:bg-white/10 border border-white/10 rounded text-slate-400 transition-colors"
|
||||
>
|
||||
{tc("actions.cancel")}
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex-1 min-h-0 overflow-y-auto pr-1 -mr-1">
|
||||
<div className="space-y-0.5">
|
||||
<p className="text-[10px] text-slate-500 mb-2 uppercase tracking-wide font-semibold">
|
||||
{t("configurable")}
|
||||
</p>
|
||||
{SHORTCUT_ACTIONS.map((action) => {
|
||||
const isCapturing = captureFor === action;
|
||||
const hasConflict = conflict?.forAction === action;
|
||||
return (
|
||||
<div key={action}>
|
||||
<div className="flex items-center justify-between py-1.5 px-1 border-b border-white/5">
|
||||
<span className="text-sm text-slate-300">{t(`actions.${action}`)}</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setConflict(null);
|
||||
setCaptureFor(isCapturing ? null : action);
|
||||
}}
|
||||
title={isCapturing ? t("pressEscToCancel") : t("clickToChange")}
|
||||
className={[
|
||||
"px-2 py-1 rounded text-xs font-mono border transition-all min-w-[90px] text-center select-none",
|
||||
isCapturing
|
||||
? "bg-[#34B27B]/20 border-[#34B27B] text-[#34B27B] animate-pulse"
|
||||
: hasConflict
|
||||
? "bg-amber-500/10 border-amber-500/50 text-amber-400"
|
||||
: "bg-white/5 border-white/10 text-slate-200 hover:border-[#34B27B]/50 hover:text-[#34B27B] cursor-pointer",
|
||||
].join(" ")}
|
||||
>
|
||||
{isCapturing ? t("pressKey") : formatBinding(draft[action], isMac)}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{hasConflict && conflict?.conflictWith.type === "configurable" && (
|
||||
<div className="flex items-center justify-between px-1 py-1.5 mb-0.5 bg-amber-500/10 border border-amber-500/20 rounded text-xs">
|
||||
<span className="text-amber-400">
|
||||
⚠{" "}
|
||||
{t("alreadyUsedBy", {
|
||||
action: t(`actions.${conflict.conflictWith.action}`),
|
||||
})}
|
||||
</span>
|
||||
<div className="flex gap-1.5">
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleSwap}
|
||||
className="px-2 py-0.5 bg-amber-500/20 hover:bg-amber-500/30 border border-amber-500/40 rounded text-amber-300 font-medium transition-colors"
|
||||
>
|
||||
{t("swap")}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleCancelConflict}
|
||||
className="px-2 py-0.5 bg-white/5 hover:bg-white/10 border border-white/10 rounded text-slate-400 transition-colors"
|
||||
>
|
||||
{tc("actions.cancel")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="space-y-0.5 mt-2">
|
||||
<p className="text-[10px] text-slate-500 mb-2 uppercase tracking-wide font-semibold">
|
||||
{t("fixed")}
|
||||
</p>
|
||||
{FIXED_SHORTCUTS.map(({ i18nKey, label, display }) => (
|
||||
<div
|
||||
key={i18nKey}
|
||||
className="flex items-center justify-between py-1.5 px-1 border-b border-white/5 last:border-0"
|
||||
>
|
||||
<span className="text-sm text-slate-400">
|
||||
{t(`fixedActions.${i18nKey}`, { defaultValue: label })}
|
||||
</span>
|
||||
<kbd className="px-2 py-1 bg-white/5 border border-white/10 rounded text-xs font-mono text-slate-400 min-w-[90px] text-center">
|
||||
{display}
|
||||
</kbd>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
))}
|
||||
</div>
|
||||
|
||||
<p className="text-[10px] text-slate-500 mt-1">{t("helpText")}</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-0.5 mt-2">
|
||||
<p className="text-[10px] text-slate-500 mb-2 uppercase tracking-wide font-semibold">
|
||||
{t("fixed")}
|
||||
</p>
|
||||
{FIXED_SHORTCUTS.map(({ i18nKey, label, display }) => (
|
||||
<div
|
||||
key={i18nKey}
|
||||
className="flex items-center justify-between py-1.5 px-1 border-b border-white/5 last:border-0"
|
||||
>
|
||||
<span className="text-sm text-slate-400">
|
||||
{t(`fixedActions.${i18nKey}`, { defaultValue: label })}
|
||||
</span>
|
||||
<kbd className="px-2 py-1 bg-white/5 border border-white/10 rounded text-xs font-mono text-slate-400 min-w-[90px] text-center">
|
||||
{display}
|
||||
</kbd>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<p className="text-[10px] text-slate-500 mt-1">{t("helpText")}</p>
|
||||
|
||||
<DialogFooter className="flex gap-2 sm:justify-between mt-2">
|
||||
<DialogFooter className="shrink-0 flex gap-2 sm:justify-between mt-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
|
||||
@@ -40,5 +40,6 @@
|
||||
"cameraDisconnected": "Webcam déconnectée.",
|
||||
"cameraNotFound": "Caméra introuvable.",
|
||||
"permissionDenied": "Permission d'enregistrement refusée. Veuillez autoriser l'enregistrement d'écran."
|
||||
}
|
||||
},
|
||||
"loadingVideo": "Chargement de la vidéo..."
|
||||
}
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
"exportFailedWithError": "エクスポートに失敗しました: {{error}}",
|
||||
"failedToSaveExport": "エクスポートの保存に失敗しました",
|
||||
"failedToSaveExportedVideo": "エクスポートしたビデオの保存に失敗しました",
|
||||
"failedToRevealInFolder": "フォルダの表示に失敗しました: {{error}}"
|
||||
"failedToRevealInFolder": "フォルダの表示に失敗しました: {{error}}",
|
||||
"exportBackgroundLoadFailed": "エクスポートに失敗しました: 背景画像を読み込めませんでした ({{url}})"
|
||||
},
|
||||
"export": {
|
||||
"canceled": "エクスポートがキャンセルされました",
|
||||
@@ -37,6 +38,8 @@
|
||||
"systemAudioUnavailable": "システムオーディオが利用できません。システムオーディオなしで録画します。",
|
||||
"microphoneDenied": "マイクのアクセスが拒否されました。オーディオなしで録画を続行します。",
|
||||
"cameraDenied": "カメラのアクセスが拒否されました。ウェブカメラなしで録画を続行します。",
|
||||
"permissionDenied": "録画の権限が拒否されました。画面録画を許可してください。"
|
||||
"permissionDenied": "録画の権限が拒否されました。画面録画を許可してください。",
|
||||
"cameraDisconnected": "ウェブカメラが切断されました。",
|
||||
"cameraNotFound": "カメラが見つかりません。"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,8 @@
|
||||
"systemAudioUnavailable": "시스템 오디오를 사용할 수 없습니다. 시스템 오디오 없이 녹화합니다.",
|
||||
"microphoneDenied": "마이크 접근이 거부되었습니다. 오디오 없이 녹화를 계속합니다.",
|
||||
"cameraDenied": "카메라 접근이 거부되었습니다. 웹캠 없이 녹화를 계속합니다.",
|
||||
"permissionDenied": "녹화 권한이 거부되었습니다. 화면 녹화를 허용해 주세요."
|
||||
"permissionDenied": "녹화 권한이 거부되었습니다. 화면 녹화를 허용해 주세요.",
|
||||
"cameraDisconnected": "웹캠 연결이 끊어졌습니다.",
|
||||
"cameraNotFound": "카메라를 찾을 수 없습니다."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,5 +33,11 @@
|
||||
"recording": {
|
||||
"selectSource": "녹화할 소스를 선택해 주세요"
|
||||
},
|
||||
"language": "언어"
|
||||
"language": "언어",
|
||||
"systemLanguagePrompt": {
|
||||
"title": "시스템 언어를 사용하시겠습니까?",
|
||||
"description": "시스템 언어가 {{language}}(으)로 감지되었습니다. OpenScreen을 {{language}}(으)로 전환하시겠습니까?",
|
||||
"switch": "{{language}}(으)로 전환",
|
||||
"keepDefault": "현재 언어 유지"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
"pictureInPicture": "화면 속 화면",
|
||||
"verticalStack": "세로 배치",
|
||||
"webcamShape": "카메라 모양",
|
||||
"webcamSize": "웹캠 크기"
|
||||
"webcamSize": "웹캠 크기",
|
||||
"dualFrame": "듀얼 프레임"
|
||||
},
|
||||
"effects": {
|
||||
"title": "비디오 효과",
|
||||
@@ -129,7 +130,20 @@
|
||||
"invalidImageType": "지원하지 않는 파일 형식입니다",
|
||||
"imageFormatsOnly": "JPG, PNG, GIF 또는 WebP 이미지 파일을 업로드해 주세요.",
|
||||
"imageUploadSuccess": "이미지가 성공적으로 업로드되었습니다!",
|
||||
"failedImageUpload": "이미지 업로드에 실패했습니다"
|
||||
"failedImageUpload": "이미지 업로드에 실패했습니다",
|
||||
"blurColor": "블러 색상",
|
||||
"blurColorBlack": "검정",
|
||||
"blurColorWhite": "흰색",
|
||||
"blurIntensity": "블러 강도",
|
||||
"blurShape": "블러 모양",
|
||||
"blurShapeFreehand": "자유 곡선",
|
||||
"blurShapeOval": "타원",
|
||||
"blurShapeRectangle": "사각형",
|
||||
"blurType": "블러 종류",
|
||||
"blurTypeBlur": "블러",
|
||||
"blurTypeMosaic": "모자이크 블러",
|
||||
"mosaicBlockSize": "모자이크 블록 크기",
|
||||
"typeBlur": "블러"
|
||||
},
|
||||
"fontStyles": {
|
||||
"classic": "클래식",
|
||||
|
||||
@@ -20,7 +20,8 @@
|
||||
"addAnnotation": "주석 추가",
|
||||
"addKeyframe": "키프레임 추가",
|
||||
"deleteSelected": "선택 항목 삭제",
|
||||
"playPause": "재생 / 일시정지"
|
||||
"playPause": "재생 / 일시정지",
|
||||
"addBlur": "블러 추가"
|
||||
},
|
||||
"fixedActions": {
|
||||
"undo": "실행 취소",
|
||||
|
||||
@@ -4,13 +4,15 @@
|
||||
"suggestZooms": "커서 기반 줌 제안",
|
||||
"addTrim": "트림 추가 (T)",
|
||||
"addAnnotation": "주석 추가 (A)",
|
||||
"addSpeed": "속도 추가 (S)"
|
||||
"addSpeed": "속도 추가 (S)",
|
||||
"addBlur": "블러 추가 (B)"
|
||||
},
|
||||
"hints": {
|
||||
"pressZoom": "Z를 눌러 줌 추가",
|
||||
"pressTrim": "T를 눌러 트림 추가",
|
||||
"pressAnnotation": "A를 눌러 주석 추가",
|
||||
"pressSpeed": "S를 눌러 속도 추가"
|
||||
"pressSpeed": "S를 눌러 속도 추가",
|
||||
"pressBlur": "B 키를 눌러 블러 영역을 추가하세요"
|
||||
},
|
||||
"labels": {
|
||||
"pan": "이동",
|
||||
@@ -22,7 +24,8 @@
|
||||
"speedItem": "속도 {{index}}",
|
||||
"annotationItem": "주석",
|
||||
"imageItem": "이미지",
|
||||
"emptyText": "빈 텍스트"
|
||||
"emptyText": "빈 텍스트",
|
||||
"blurItem": "블러 {{index}}"
|
||||
},
|
||||
"emptyState": {
|
||||
"noVideo": "불러온 비디오 없음",
|
||||
|
||||
@@ -31,6 +31,15 @@
|
||||
"systemAudioUnavailable": "Sistem sesi kullanılamıyor. Sistem sesi olmadan kaydediliyor.",
|
||||
"microphoneDenied": "Mikrofon erişimi reddedildi. Kayıt ses olmadan devam edecek.",
|
||||
"cameraDenied": "Kamera erişimi reddedildi. Kayıt kamera olmadan devam edecek.",
|
||||
"permissionDenied": "Kayıt izni reddedildi. Lütfen ekran kaydına izin verin."
|
||||
"permissionDenied": "Kayıt izni reddedildi. Lütfen ekran kaydına izin verin.",
|
||||
"cameraDisconnected": "Webcam bağlantısı kesildi.",
|
||||
"cameraNotFound": "Kamera bulunamadı."
|
||||
},
|
||||
"loadingVideo": "Video yükleniyor...",
|
||||
"newRecording": {
|
||||
"title": "Kaydediciye Dön",
|
||||
"description": "Mevcut oturumunuz kaydedildi.",
|
||||
"cancel": "İptal",
|
||||
"confirm": "Onayla"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,5 +44,11 @@
|
||||
"recording": {
|
||||
"selectSource": "Lütfen kayıt için bir kaynak seçin"
|
||||
},
|
||||
"language": "Dil"
|
||||
"language": "Dil",
|
||||
"systemLanguagePrompt": {
|
||||
"title": "Sistem dilinizi kullanmak ister misiniz?",
|
||||
"description": "Sistem diliniz {{language}} olarak algılandı. OpenScreen i {{language}} diline geçirmek ister misiniz?",
|
||||
"switch": "{{language}} diline geç",
|
||||
"keepDefault": "Mevcut dili koru"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,9 @@
|
||||
"speed": {
|
||||
"playbackSpeed": "Oynatma Hızı",
|
||||
"selectRegion": "Ayarlamak için bir hız bölgesi seçin",
|
||||
"deleteRegion": "Hız Bölgesini Sil"
|
||||
"deleteRegion": "Hız Bölgesini Sil",
|
||||
"customPlaybackSpeed": "Özel Oynatma Hızı",
|
||||
"maxSpeedError": "Hız 16× değerinden yüksek olamaz"
|
||||
},
|
||||
"trim": {
|
||||
"deleteRegion": "Kırpma Bölgesini Sil"
|
||||
@@ -24,7 +26,9 @@
|
||||
"selectPreset": "Ön ayar seçin",
|
||||
"pictureInPicture": "Resim İçinde Resim",
|
||||
"verticalStack": "Dikey Yığın",
|
||||
"webcamShape": "Kamera Şekli"
|
||||
"webcamShape": "Kamera Şekli",
|
||||
"dualFrame": "Çift Kare",
|
||||
"webcamSize": "Webcam Boyutu"
|
||||
},
|
||||
"effects": {
|
||||
"title": "Video Efektleri",
|
||||
@@ -132,7 +136,14 @@
|
||||
"invalidImageType": "Geçersiz dosya türü",
|
||||
"imageFormatsOnly": "Lütfen bir JPG, PNG, GIF veya WebP görüntü dosyası yükleyin.",
|
||||
"imageUploadSuccess": "Görüntü başarıyla yüklendi!",
|
||||
"failedImageUpload": "Görüntü yüklenemedi"
|
||||
"failedImageUpload": "Görüntü yüklenemedi",
|
||||
"blurColor": "Bulanıklık Rengi",
|
||||
"blurColorBlack": "Siyah",
|
||||
"blurColorWhite": "Beyaz",
|
||||
"blurType": "Bulanıklık Türü",
|
||||
"blurTypeBlur": "Bulanık",
|
||||
"blurTypeMosaic": "Mozaik Bulanıklık",
|
||||
"mosaicBlockSize": "Mozaik Blok Boyutu"
|
||||
},
|
||||
"fontStyles": {
|
||||
"classic": "Klasik",
|
||||
|
||||
@@ -136,7 +136,14 @@
|
||||
"invalidImageType": "无效的文件类型",
|
||||
"imageFormatsOnly": "请上传 JPG、PNG、GIF 或 WebP 格式的图片文件。",
|
||||
"imageUploadSuccess": "图片上传成功!",
|
||||
"failedImageUpload": "上传图片失败"
|
||||
"failedImageUpload": "上传图片失败",
|
||||
"blurColor": "模糊颜色",
|
||||
"blurColorBlack": "黑色",
|
||||
"blurColorWhite": "白色",
|
||||
"blurType": "模糊类型",
|
||||
"blurTypeBlur": "模糊",
|
||||
"blurTypeMosaic": "马赛克模糊",
|
||||
"mosaicBlockSize": "马赛克块大小"
|
||||
},
|
||||
"fontStyles": {
|
||||
"classic": "经典",
|
||||
|
||||
@@ -38,6 +38,8 @@
|
||||
"systemAudioUnavailable": "系統音訊不可用。將在無系統音訊的情況下錄製。",
|
||||
"microphoneDenied": "麥克風權限被拒絕。錄製將繼續,但不包含音訊。",
|
||||
"cameraDenied": "攝影機權限被拒絕。錄製將繼續,但不包含攝影機畫面。",
|
||||
"permissionDenied": "錄影權限被拒絕。請允許螢幕錄製。"
|
||||
"permissionDenied": "錄影權限被拒絕。請允許螢幕錄製。",
|
||||
"cameraDisconnected": "網路攝影機已中斷連線。",
|
||||
"cameraNotFound": "找不到攝影機。"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,5 +33,11 @@
|
||||
"recording": {
|
||||
"selectSource": "請選擇要錄製的來源"
|
||||
},
|
||||
"language": "語言"
|
||||
"language": "語言",
|
||||
"systemLanguagePrompt": {
|
||||
"title": "要使用系統語言嗎?",
|
||||
"description": "偵測到系統語言為 {{language}}。要將 OpenScreen 切換為 {{language}} 嗎?",
|
||||
"switch": "切換為 {{language}}",
|
||||
"keepDefault": "保持目前語言"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +143,14 @@
|
||||
"invalidImageType": "無效的檔案類型",
|
||||
"imageFormatsOnly": "請上傳 JPG、PNG、GIF 或 WebP 格式的圖片檔案。",
|
||||
"imageUploadSuccess": "圖片上傳成功!",
|
||||
"failedImageUpload": "上傳圖片失敗"
|
||||
"failedImageUpload": "上傳圖片失敗",
|
||||
"blurColor": "模糊顏色",
|
||||
"blurColorBlack": "黑色",
|
||||
"blurColorWhite": "白色",
|
||||
"blurType": "模糊類型",
|
||||
"blurTypeBlur": "模糊",
|
||||
"blurTypeMosaic": "馬賽克模糊",
|
||||
"mosaicBlockSize": "馬賽克區塊大小"
|
||||
},
|
||||
"fontStyles": {
|
||||
"classic": "經典",
|
||||
|
||||
Reference in New Issue
Block a user