dsh-cait 1.1.6 → 1.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +43 -0
- package/README.ja.md +20 -10
- package/README.md +91 -72
- package/README.zh-CN.md +20 -10
- package/client/components.js +126 -13
- package/client/locales.js +74 -2
- package/client/styles.js +6 -2
- package/client.js +206 -17
- package/lib/aligner.js +6 -6
- package/lib/api.js +6 -0
- package/lib/docx.js +124 -26
- package/lib/import-export.js +115 -10
- package/lib/localization.js +1098 -0
- package/lib/persistence.js +14 -0
- package/lib/pptx.js +33 -1
- package/lib/qa.js +18 -0
- package/lib/store-utils.js +8 -3
- package/lib/store.js +14 -2
- package/lib/subtitles.js +213 -0
- package/lib/xlsx.js +176 -66
- package/package.json +1 -1
package/client/components.js
CHANGED
|
@@ -761,9 +761,21 @@
|
|
|
761
761
|
)
|
|
762
762
|
}
|
|
763
763
|
|
|
764
|
-
function EditorPane({ t, doc, index, segment, detail, draft, setDraft, dirty, busy, onConfirm, onUnconfirm, onNext, onPrev, onSave, onUndo, onRedo, canUndo, canRedo, onToggleLock, onMerge, onSplit, onAdopt, onApplyTerm, onOpenBrief, onOpenResources, onOpenAnalysis, onOpenNotes, noteCount, qaRisk, textareaRef }) {
|
|
764
|
+
function EditorPane({ t, doc, index, segment, detail, draft, setDraft, dirty, busy, onConfirm, onUnconfirm, onNext, onPrev, onSave, onUndo, onRedo, canUndo, canRedo, onToggleLock, onMerge, onSplit, onMergeCue, onSplitCue, onShiftSubtitles, onAdopt, onApplyTerm, onOpenBrief, onOpenResources, onOpenAnalysis, onOpenNotes, noteCount, qaRisk, textareaRef }) {
|
|
765
765
|
const hits = detail?.index === index ? detail.glossaryHits : []
|
|
766
766
|
const charCount = Array.from(draft || '').length
|
|
767
|
+
const isSubtitle = doc.format === 'srt' || doc.format === 'vtt'
|
|
768
|
+
const [shiftSeconds, setShiftSeconds] = useState('1')
|
|
769
|
+
const cueStart = Number(segment.meta?.start)
|
|
770
|
+
const cueEnd = Number(segment.meta?.end)
|
|
771
|
+
const formatCue = (value) => {
|
|
772
|
+
if (!Number.isFinite(value)) return '--:--.-'
|
|
773
|
+
const total = Math.max(0, Math.round(value))
|
|
774
|
+
const minutes = Math.floor(total / 60000)
|
|
775
|
+
const seconds = Math.floor((total % 60000) / 1000)
|
|
776
|
+
const millis = total % 1000
|
|
777
|
+
return `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}.${String(millis).padStart(3, '0')}`
|
|
778
|
+
}
|
|
767
779
|
const progress = doc.progress
|
|
768
780
|
const remaining = progress.total - progress.confirmed
|
|
769
781
|
const statusLabel =
|
|
@@ -1031,16 +1043,47 @@
|
|
|
1031
1043
|
h(Icon, { name: 'chevronLeft', size: 13 }),
|
|
1032
1044
|
t('editor.prev'),
|
|
1033
1045
|
),
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1046
|
+
isSubtitle
|
|
1047
|
+
? h(
|
|
1048
|
+
'div',
|
|
1049
|
+
{ className: 'cait-subbar' },
|
|
1050
|
+
h('span', { className: 'cait-subtime' }, `${formatCue(cueStart)} → ${formatCue(cueEnd)}`),
|
|
1051
|
+
h('input', {
|
|
1052
|
+
className: 'cait-suboffset',
|
|
1053
|
+
type: 'number',
|
|
1054
|
+
step: '0.1',
|
|
1055
|
+
value: shiftSeconds,
|
|
1056
|
+
onChange: (event) => setShiftSeconds(event.target.value),
|
|
1057
|
+
title: t('editor.shiftHint'),
|
|
1058
|
+
}),
|
|
1059
|
+
h(
|
|
1060
|
+
'button',
|
|
1061
|
+
{ className: 'cait-btn cait-btn-ghost', onClick: () => onShiftSubtitles(Math.round(Number(shiftSeconds) * 1000)), disabled: Boolean(busy) || Boolean(segment.locked) || !Number.isFinite(Number(shiftSeconds)) },
|
|
1062
|
+
t('editor.shift'),
|
|
1063
|
+
),
|
|
1064
|
+
h(
|
|
1065
|
+
'button',
|
|
1066
|
+
{ className: 'cait-btn cait-btn-ghost', onClick: onMergeCue, disabled: Boolean(busy) || Boolean(segment.locked) || index >= doc.segments.length - 1 },
|
|
1067
|
+
t('editor.mergeCue'),
|
|
1068
|
+
),
|
|
1069
|
+
h(
|
|
1070
|
+
'button',
|
|
1071
|
+
{ className: 'cait-btn cait-btn-ghost', onClick: () => onSplitCue(Math.round((cueStart + cueEnd) / 2)), disabled: Boolean(busy) || Boolean(segment.locked) || !(cueEnd > cueStart) },
|
|
1072
|
+
t('editor.splitCue'),
|
|
1073
|
+
),
|
|
1074
|
+
)
|
|
1075
|
+
: [
|
|
1076
|
+
h(
|
|
1077
|
+
'button',
|
|
1078
|
+
{ key: 'merge', className: 'cait-btn cait-btn-ghost', onClick: onMerge, disabled: Boolean(busy) || !canMerge, title: t('editor.mergeHint') },
|
|
1079
|
+
t('editor.merge'),
|
|
1080
|
+
),
|
|
1081
|
+
h(
|
|
1082
|
+
'button',
|
|
1083
|
+
{ key: 'split', className: 'cait-btn cait-btn-ghost', onClick: splitAtCursor, disabled: Boolean(busy) || !canSplit, title: t('editor.splitHint') },
|
|
1084
|
+
t('editor.split'),
|
|
1085
|
+
),
|
|
1086
|
+
],
|
|
1044
1087
|
h(
|
|
1045
1088
|
'button',
|
|
1046
1089
|
{ className: 'cait-iconbtn', onClick: onUndo, disabled: Boolean(busy) || !canUndo, title: t('editor.undo'), 'aria-label': t('editor.undo') },
|
|
@@ -1899,6 +1942,7 @@
|
|
|
1899
1942
|
const sourceRef = useRef(null)
|
|
1900
1943
|
const targetRef = useRef(null)
|
|
1901
1944
|
const libraries = data?.tmLibraries || []
|
|
1945
|
+
const languageOptions = ['zh-CN', 'zh-TW', 'en', 'ja', 'ko', 'fr', 'de', 'es', 'pt-BR', 'ru', 'it', 'ar']
|
|
1902
1946
|
|
|
1903
1947
|
useEffect(() => {
|
|
1904
1948
|
if (!tmId && libraries.length) setTmId(libraries[0].id)
|
|
@@ -1999,7 +2043,12 @@
|
|
|
1999
2043
|
action: async () => {
|
|
2000
2044
|
setWorking(true)
|
|
2001
2045
|
try {
|
|
2002
|
-
const written = await rpc('writeAlignment', {
|
|
2046
|
+
const written = await rpc('writeAlignment', {
|
|
2047
|
+
pairs,
|
|
2048
|
+
tmId: tmId || undefined,
|
|
2049
|
+
sourceLang: sourceLang || undefined,
|
|
2050
|
+
targetLang: targetLang || undefined,
|
|
2051
|
+
})
|
|
2003
2052
|
notify(t('align.written', { written: written.written, skipped: written.skipped }))
|
|
2004
2053
|
} catch (error) {
|
|
2005
2054
|
report(error)
|
|
@@ -2047,15 +2096,20 @@
|
|
|
2047
2096
|
h(
|
|
2048
2097
|
'div',
|
|
2049
2098
|
{ className: 'cait-alignactions' },
|
|
2099
|
+
h('datalist', { id: 'cait-align-languages' }, languageOptions.map((code) => h('option', { key: code, value: code }))),
|
|
2050
2100
|
h('input', {
|
|
2051
2101
|
className: 'cait-libinput',
|
|
2052
2102
|
placeholder: t('align.sourceLang'),
|
|
2103
|
+
title: t('align.langHint'),
|
|
2104
|
+
list: 'cait-align-languages',
|
|
2053
2105
|
value: sourceLang,
|
|
2054
2106
|
onChange: (event) => setSourceLang(event.target.value),
|
|
2055
2107
|
}),
|
|
2056
2108
|
h('input', {
|
|
2057
2109
|
className: 'cait-libinput',
|
|
2058
2110
|
placeholder: t('align.targetLang'),
|
|
2111
|
+
title: t('align.langHint'),
|
|
2112
|
+
list: 'cait-align-languages',
|
|
2059
2113
|
value: targetLang,
|
|
2060
2114
|
onChange: (event) => setTargetLang(event.target.value),
|
|
2061
2115
|
}),
|
|
@@ -2065,8 +2119,15 @@
|
|
|
2065
2119
|
className: 'cait-select cait-liblang',
|
|
2066
2120
|
value: tmId,
|
|
2067
2121
|
onChange: (event) => setTmId(event.target.value),
|
|
2122
|
+
title: t('align.tmHint'),
|
|
2123
|
+
'aria-label': t('align.tm'),
|
|
2124
|
+
disabled: !libraries.length,
|
|
2068
2125
|
},
|
|
2069
|
-
libraries.
|
|
2126
|
+
libraries.length
|
|
2127
|
+
? libraries.map((library) =>
|
|
2128
|
+
h('option', { key: library.id, value: library.id }, t('align.tmOption', { name: library.name })),
|
|
2129
|
+
)
|
|
2130
|
+
: h('option', { value: '' }, t('align.noTm')),
|
|
2070
2131
|
),
|
|
2071
2132
|
h(
|
|
2072
2133
|
'button',
|
|
@@ -3156,6 +3217,13 @@
|
|
|
3156
3217
|
['srt', t('modal.export.srt'), t('modal.export.subtitleGroup'), 'file'],
|
|
3157
3218
|
['vtt', t('modal.export.vtt'), t('modal.export.subtitleGroup'), 'file'],
|
|
3158
3219
|
['po', t('modal.export.po'), t('modal.export.localizationGroup'), 'file'],
|
|
3220
|
+
['apple-strings', t('modal.export.appleStrings'), t('modal.export.localizationGroup'), 'file'],
|
|
3221
|
+
['apple-stringsdict', t('modal.export.appleStringsdict'), t('modal.export.localizationGroup'), 'file'],
|
|
3222
|
+
['android', t('modal.export.android'), t('modal.export.localizationGroup'), 'file'],
|
|
3223
|
+
['resx', t('modal.export.resx'), t('modal.export.localizationGroup'), 'file'],
|
|
3224
|
+
['arb', t('modal.export.arb'), t('modal.export.localizationGroup'), 'file'],
|
|
3225
|
+
['yaml', t('modal.export.yaml'), t('modal.export.localizationGroup'), 'file'],
|
|
3226
|
+
['properties', t('modal.export.properties'), t('modal.export.localizationGroup'), 'file'],
|
|
3159
3227
|
['xliff', t('modal.export.xliff'), t('modal.export.localizationGroup'), 'file'],
|
|
3160
3228
|
['xliff-review', t('modal.export.xliffReview'), t('modal.export.localizationGroup'), 'file'],
|
|
3161
3229
|
['xliff2', t('modal.export.xliff2'), t('modal.export.localizationGroup'), 'file'],
|
|
@@ -4932,6 +5000,48 @@
|
|
|
4932
5000
|
})
|
|
4933
5001
|
}
|
|
4934
5002
|
|
|
5003
|
+
const mergeCue = () => {
|
|
5004
|
+
if (!doc || !segment || busy) return
|
|
5005
|
+
openConfirm({
|
|
5006
|
+
title: t('modal.confirm.mergeTitle'),
|
|
5007
|
+
message: t('modal.confirm.mergeCue'),
|
|
5008
|
+
confirmLabel: t('editor.mergeCue'),
|
|
5009
|
+
action: async () => {
|
|
5010
|
+
const result = await rpc('mergeCues', { documentId: doc.id, index: selected, count: 2 })
|
|
5011
|
+
adoptState(result.state, result.index)
|
|
5012
|
+
notify(t('toast.cuesMerged'))
|
|
5013
|
+
},
|
|
5014
|
+
})
|
|
5015
|
+
}
|
|
5016
|
+
|
|
5017
|
+
const splitCueAt = (at) => {
|
|
5018
|
+
if (!doc || !segment || busy) return
|
|
5019
|
+
openConfirm({
|
|
5020
|
+
title: t('modal.confirm.splitTitle'),
|
|
5021
|
+
message: t('modal.confirm.splitCue'),
|
|
5022
|
+
confirmLabel: t('editor.splitCue'),
|
|
5023
|
+
action: async () => {
|
|
5024
|
+
const result = await rpc('splitCue', { documentId: doc.id, index: selected, at })
|
|
5025
|
+
adoptState(result.state, result.index)
|
|
5026
|
+
notify(t('toast.cueSplit'))
|
|
5027
|
+
},
|
|
5028
|
+
})
|
|
5029
|
+
}
|
|
5030
|
+
|
|
5031
|
+
const shiftSubtitles = (offsetMs) => {
|
|
5032
|
+
if (!doc || !offsetMs || busy) return
|
|
5033
|
+
openConfirm({
|
|
5034
|
+
title: t('modal.confirm.shiftTitle'),
|
|
5035
|
+
message: t('modal.confirm.shiftCue'),
|
|
5036
|
+
confirmLabel: t('editor.shift'),
|
|
5037
|
+
action: async () => {
|
|
5038
|
+
const result = await rpc('shiftSubtitles', { documentId: doc.id, offsetMs })
|
|
5039
|
+
adoptState(result.state)
|
|
5040
|
+
notify(t('toast.subtitlesShifted'))
|
|
5041
|
+
},
|
|
5042
|
+
})
|
|
5043
|
+
}
|
|
5044
|
+
|
|
4935
5045
|
const saveBrief = async (brief) => {
|
|
4936
5046
|
if (!doc || busy) return false
|
|
4937
5047
|
setBusy('brief')
|
|
@@ -5799,6 +5909,9 @@
|
|
|
5799
5909
|
onToggleLock: toggleLock,
|
|
5800
5910
|
onMerge: mergeCurrent,
|
|
5801
5911
|
onSplit: splitCurrent,
|
|
5912
|
+
onMergeCue: mergeCue,
|
|
5913
|
+
onSplitCue: splitCueAt,
|
|
5914
|
+
onShiftSubtitles: shiftSubtitles,
|
|
5802
5915
|
onNext: () => moveSegment(1),
|
|
5803
5916
|
onPrev: () => moveSegment(-1),
|
|
5804
5917
|
onSave: () => saveDraft(false),
|
package/client/locales.js
CHANGED
|
@@ -219,6 +219,13 @@
|
|
|
219
219
|
'modal.export.srt': 'SRT 字幕',
|
|
220
220
|
'modal.export.vtt': 'WebVTT 字幕',
|
|
221
221
|
'modal.export.po': 'PO 本地化',
|
|
222
|
+
'modal.export.appleStrings': '.strings 字符串',
|
|
223
|
+
'modal.export.appleStringsdict': '.stringsdict 复数',
|
|
224
|
+
'modal.export.android': 'Android strings.xml',
|
|
225
|
+
'modal.export.resx': '.resx 资源',
|
|
226
|
+
'modal.export.arb': 'ARB 应用资源',
|
|
227
|
+
'modal.export.yaml': 'YAML 本地化',
|
|
228
|
+
'modal.export.properties': '.properties 属性',
|
|
222
229
|
'modal.export.xliff': 'XLIFF 1.2',
|
|
223
230
|
'modal.export.xliffReview': 'XLIFF 审校包',
|
|
224
231
|
'modal.export.reviewImport': '回填审校包',
|
|
@@ -364,6 +371,11 @@
|
|
|
364
371
|
'align.readError': '读取文件失败。',
|
|
365
372
|
'align.sourceLang': '源语言(可选)',
|
|
366
373
|
'align.targetLang': '目标语言(可选)',
|
|
374
|
+
'align.langHint': '可留空;例如 zh-CN、en',
|
|
375
|
+
'align.tm': '写入记忆库',
|
|
376
|
+
'align.tmHint': '对齐结果写入这个记忆库',
|
|
377
|
+
'align.tmOption': '记忆库:{name}',
|
|
378
|
+
'align.noTm': '暂无已挂载的记忆库',
|
|
367
379
|
'align.stats': '高置信 {high} · 待确认 {medium} · 可疑 {low}',
|
|
368
380
|
'align.unpairedSource': '未配对原文',
|
|
369
381
|
'align.unpairedTarget': '未配对译文',
|
|
@@ -613,6 +625,13 @@
|
|
|
613
625
|
'modal.export.srt': 'SRT subtitles',
|
|
614
626
|
'modal.export.vtt': 'WebVTT subtitles',
|
|
615
627
|
'modal.export.po': 'PO localization',
|
|
628
|
+
'modal.export.appleStrings': '.strings strings',
|
|
629
|
+
'modal.export.appleStringsdict': '.stringsdict plurals',
|
|
630
|
+
'modal.export.android': 'Android strings.xml',
|
|
631
|
+
'modal.export.resx': '.resx resources',
|
|
632
|
+
'modal.export.arb': 'ARB app resources',
|
|
633
|
+
'modal.export.yaml': 'YAML localization',
|
|
634
|
+
'modal.export.properties': '.properties',
|
|
616
635
|
'modal.export.xliff': 'XLIFF 1.2',
|
|
617
636
|
'modal.export.xliffReview': 'XLIFF review package',
|
|
618
637
|
'modal.export.reviewImport': 'Apply review package',
|
|
@@ -757,8 +776,13 @@
|
|
|
757
776
|
'align.running': 'Aligning…',
|
|
758
777
|
'align.needFiles': 'Choose both files first.',
|
|
759
778
|
'align.readError': 'Could not read that file.',
|
|
760
|
-
'align.sourceLang': 'Source language
|
|
761
|
-
'align.targetLang': 'Target language
|
|
779
|
+
'align.sourceLang': 'Source language',
|
|
780
|
+
'align.targetLang': 'Target language',
|
|
781
|
+
'align.langHint': 'Optional; for example zh-CN, en',
|
|
782
|
+
'align.tm': 'Write to memory',
|
|
783
|
+
'align.tmHint': 'Aligned pairs are written to this memory',
|
|
784
|
+
'align.tmOption': 'Memory: {name}',
|
|
785
|
+
'align.noTm': 'No mounted memory yet',
|
|
762
786
|
'align.stats': 'high {high} · to review {medium} · doubtful {low}',
|
|
763
787
|
'align.unpairedSource': 'Unpaired source',
|
|
764
788
|
'align.unpairedTarget': 'Unpaired target',
|
|
@@ -1008,6 +1032,13 @@
|
|
|
1008
1032
|
'modal.export.srt': 'SRT 字幕',
|
|
1009
1033
|
'modal.export.vtt': 'WebVTT 字幕',
|
|
1010
1034
|
'modal.export.po': 'PO ローカライズ',
|
|
1035
|
+
'modal.export.appleStrings': '.strings 文字列',
|
|
1036
|
+
'modal.export.appleStringsdict': '.stringsdict 複数形',
|
|
1037
|
+
'modal.export.android': 'Android strings.xml',
|
|
1038
|
+
'modal.export.resx': '.resx リソース',
|
|
1039
|
+
'modal.export.arb': 'ARB アプリリソース',
|
|
1040
|
+
'modal.export.yaml': 'YAML ローカライズ',
|
|
1041
|
+
'modal.export.properties': '.properties',
|
|
1011
1042
|
'modal.export.xliff': 'XLIFF 1.2',
|
|
1012
1043
|
'modal.export.xliffReview': 'XLIFF 校閲パッケージ',
|
|
1013
1044
|
'modal.export.reviewImport': '校閲パッケージを反映',
|
|
@@ -1154,6 +1185,11 @@
|
|
|
1154
1185
|
'align.readError': 'ファイルを読み込めませんでした。',
|
|
1155
1186
|
'align.sourceLang': '原文言語(任意)',
|
|
1156
1187
|
'align.targetLang': '訳文言語(任意)',
|
|
1188
|
+
'align.langHint': '任意;例: zh-CN、en',
|
|
1189
|
+
'align.tm': '保存先メモリ',
|
|
1190
|
+
'align.tmHint': '整列結果をこのメモリに保存します',
|
|
1191
|
+
'align.tmOption': 'メモリ: {name}',
|
|
1192
|
+
'align.noTm': 'メモリ未設定',
|
|
1157
1193
|
'align.stats': '高 {high} · 要確認 {medium} · 要注意 {low}',
|
|
1158
1194
|
'align.unpairedSource': '未対応の原文',
|
|
1159
1195
|
'align.unpairedTarget': '未対応の訳文',
|
|
@@ -1239,6 +1275,7 @@
|
|
|
1239
1275
|
'qa.risk': '风险',
|
|
1240
1276
|
'qa.code.TERM_FORBIDDEN': '禁用术语',
|
|
1241
1277
|
'qa.code.LENGTH_DRIFT': '长度异常',
|
|
1278
|
+
'qa.code.SUBTITLE_OVERLAP': '字幕重叠',
|
|
1242
1279
|
'qa.fixCopy': '复制源文',
|
|
1243
1280
|
'qa.fixClear': '清空译文',
|
|
1244
1281
|
'qa.fixTerm': '插入术语',
|
|
@@ -1391,6 +1428,7 @@
|
|
|
1391
1428
|
'qa.risk': 'Risk',
|
|
1392
1429
|
'qa.code.TERM_FORBIDDEN': 'Forbidden term',
|
|
1393
1430
|
'qa.code.LENGTH_DRIFT': 'Length drift',
|
|
1431
|
+
'qa.code.SUBTITLE_OVERLAP': 'Subtitle overlap',
|
|
1394
1432
|
'qa.fixCopy': 'Copy source',
|
|
1395
1433
|
'qa.fixClear': 'Clear target',
|
|
1396
1434
|
'qa.fixTerm': 'Insert term',
|
|
@@ -1543,6 +1581,7 @@
|
|
|
1543
1581
|
'qa.risk': 'リスク',
|
|
1544
1582
|
'qa.code.TERM_FORBIDDEN': '禁止用語',
|
|
1545
1583
|
'qa.code.LENGTH_DRIFT': '長さの不一致',
|
|
1584
|
+
'qa.code.SUBTITLE_OVERLAP': '字幕の重なり',
|
|
1546
1585
|
'qa.fixCopy': '原文をコピー',
|
|
1547
1586
|
'qa.fixClear': '訳文を空にする',
|
|
1548
1587
|
'qa.fixTerm': '用語を挿入',
|
|
@@ -1655,6 +1694,17 @@
|
|
|
1655
1694
|
'modal.confirm.merge': '合并会新建一个句段,并丢失两条原文各自的确认状态。确定继续?',
|
|
1656
1695
|
'modal.confirm.splitTitle': '拆分句段',
|
|
1657
1696
|
'modal.confirm.split': '拆分后两侧都会变成未确认状态,译文按比例切开。确定继续?',
|
|
1697
|
+
'editor.shift': '平移',
|
|
1698
|
+
'editor.shiftHint': '按秒平移当前文档所有字幕时间码;不能产生负时间码。',
|
|
1699
|
+
'editor.mergeCue': '合并下一条',
|
|
1700
|
+
'editor.splitCue': '按中点拆分',
|
|
1701
|
+
'toast.cuesMerged': '已合并字幕',
|
|
1702
|
+
'toast.cueSplit': '已拆分字幕',
|
|
1703
|
+
'toast.subtitlesShifted': '已平移字幕时间轴',
|
|
1704
|
+
'modal.confirm.shiftTitle': '平移字幕',
|
|
1705
|
+
'modal.confirm.shiftCue': '会平移当前文档所有字幕的时间码,且不修改译文。确定继续?',
|
|
1706
|
+
'modal.confirm.mergeCue': '合并当前字幕和下一条,时间范围取两者首尾;确定继续?',
|
|
1707
|
+
'modal.confirm.splitCue': '会在当前字幕时间范围的中点拆成两条,译文按比例切开;确定继续?',
|
|
1658
1708
|
})
|
|
1659
1709
|
Object.assign(EN, {
|
|
1660
1710
|
'editor.merge': 'Merge next',
|
|
@@ -1667,6 +1717,17 @@
|
|
|
1667
1717
|
'modal.confirm.merge': 'Merging creates a new segment and discards the confirmation state of both originals. Continue?',
|
|
1668
1718
|
'modal.confirm.splitTitle': 'Split segment',
|
|
1669
1719
|
'modal.confirm.split': 'Both halves become unconfirmed and the target is split proportionally. Continue?',
|
|
1720
|
+
'editor.shift': 'Shift',
|
|
1721
|
+
'editor.shiftHint': 'Shift every subtitle cue in this document by seconds; negative timecodes are rejected.',
|
|
1722
|
+
'editor.mergeCue': 'Merge next cue',
|
|
1723
|
+
'editor.splitCue': 'Split at midpoint',
|
|
1724
|
+
'toast.cuesMerged': 'Cues merged',
|
|
1725
|
+
'toast.cueSplit': 'Cue split',
|
|
1726
|
+
'toast.subtitlesShifted': 'Subtitle timeline shifted',
|
|
1727
|
+
'modal.confirm.shiftTitle': 'Shift subtitles',
|
|
1728
|
+
'modal.confirm.shiftCue': 'Every cue in this document moves by the same offset. Translations are unchanged. Continue?',
|
|
1729
|
+
'modal.confirm.mergeCue': 'Merge this cue with the next one, using the outer time range. Continue?',
|
|
1730
|
+
'modal.confirm.splitCue': 'Split this cue at its midpoint; the translation is split proportionally. Continue?',
|
|
1670
1731
|
})
|
|
1671
1732
|
Object.assign(JA, {
|
|
1672
1733
|
'editor.merge': '次を結合',
|
|
@@ -1679,6 +1740,17 @@
|
|
|
1679
1740
|
'modal.confirm.merge': '結合すると新しいセグメントになり、元の確認状態は失われます。続行しますか?',
|
|
1680
1741
|
'modal.confirm.splitTitle': 'セグメントを分割',
|
|
1681
1742
|
'modal.confirm.split': '分割後は両側とも未確認になり、訳文は比率で分割されます。続行しますか?',
|
|
1743
|
+
'editor.shift': 'シフト',
|
|
1744
|
+
'editor.shiftHint': '文書内の全字幕を秒単位でシフトします。負のタイムコードは拒否されます。',
|
|
1745
|
+
'editor.mergeCue': '次のキューを結合',
|
|
1746
|
+
'editor.splitCue': '中点で分割',
|
|
1747
|
+
'toast.cuesMerged': '字幕を結合しました',
|
|
1748
|
+
'toast.cueSplit': '字幕を分割しました',
|
|
1749
|
+
'toast.subtitlesShifted': '字幕タイムラインをシフトしました',
|
|
1750
|
+
'modal.confirm.shiftTitle': '字幕をシフト',
|
|
1751
|
+
'modal.confirm.shiftCue': '文書内の全キューを同じ量だけ移動します。訳文は変更しません。続行しますか?',
|
|
1752
|
+
'modal.confirm.mergeCue': '現在のキューと次を結合し、外側の時間範囲を使います。続行しますか?',
|
|
1753
|
+
'modal.confirm.splitCue': '現在のキューの中央で二分割し、訳文を比率で分けます。続行しますか?',
|
|
1682
1754
|
})
|
|
1683
1755
|
const UI_LANGS = [
|
|
1684
1756
|
['auto', ''],
|
package/client/styles.js
CHANGED
|
@@ -221,6 +221,9 @@
|
|
|
221
221
|
.cait-textarea::placeholder{color:var(--dsw-alias-label-secondary)}
|
|
222
222
|
.cait-meta{font-size:10.5px;color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums}
|
|
223
223
|
.cait-actionbar{display:flex;align-items:center;gap:7px;flex-wrap:wrap;margin-top:13px}
|
|
224
|
+
.cait-subbar{display:inline-flex;align-items:center;gap:6px;flex-wrap:wrap;padding:3px 6px;border:1px solid var(--dsw-alias-border-l1);border-radius:7px;background:var(--dsw-alias-bg-layer-2)}
|
|
225
|
+
.cait-subtime{font-size:10.5px;font-variant-numeric:tabular-nums;color:var(--dsw-alias-label-secondary);white-space:nowrap}
|
|
226
|
+
.cait-suboffset{width:54px;height:24px;padding:0 5px;border:1px solid var(--dsw-alias-border-l1);border-radius:5px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font-size:10.5px}
|
|
224
227
|
.cait-hint{margin-left:auto;font-size:10px;color:var(--dsw-alias-label-secondary)}
|
|
225
228
|
.cait-savebtn{width:31px;height:31px;border-color:var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1)}
|
|
226
229
|
.cait-editor-foot{display:flex;align-items:center;gap:8px;margin-top:11px;font-size:10.5px;color:var(--dsw-alias-label-secondary)}
|
|
@@ -422,7 +425,8 @@
|
|
|
422
425
|
.cait-libtoolbar{display:flex;align-items:center;gap:6px;padding:9px 12px 6px;flex-wrap:wrap;flex:0 0 auto}
|
|
423
426
|
.cait-libsearch{display:flex;align-items:center;gap:6px;min-width:150px;flex:1;height:29px;padding:0 9px;border:1px solid var(--dsw-alias-border-l1);border-radius:7px;background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-secondary)}
|
|
424
427
|
.cait-libsearch input{min-width:0;flex:1;border:0;outline:0;background:transparent;font-size:11.5px;color:var(--dsw-alias-label-primary)}
|
|
425
|
-
.cait-liblang{height:29px;padding:0
|
|
428
|
+
.cait-liblang{height:29px;padding:0 7px;border:1px solid var(--dsw-alias-border-l1);border-radius:7px;background:var(--dsw-alias-bg-layer-1);font-size:11px;flex:0 0 auto;min-width:132px;max-width:280px}
|
|
429
|
+
.cait-liblang:disabled{opacity:.65}
|
|
426
430
|
.cait-libwrite{display:inline-flex;align-items:center;gap:5px;flex:0 0 auto;font-size:10.5px;color:var(--dsw-alias-label-secondary);white-space:nowrap}
|
|
427
431
|
.cait-libwritelabel{font-size:10.5px;color:var(--dsw-alias-label-secondary)}
|
|
428
432
|
.cait-libscope{display:flex;align-items:center;gap:4px;padding:0 12px 8px;flex-wrap:wrap;flex:0 0 auto}
|
|
@@ -455,7 +459,7 @@
|
|
|
455
459
|
.cait-alignhead{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;flex-wrap:wrap}
|
|
456
460
|
.cait-alignhead h2{margin:0 0 3px;font-size:13.5px;font-weight:600;color:var(--dsw-alias-label-primary)}
|
|
457
461
|
.cait-alignactions{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
|
|
458
|
-
.cait-alignactions .cait-libinput{width:
|
|
462
|
+
.cait-alignactions .cait-libinput{width:170px;min-height:28px;height:28px;flex:0 0 auto}
|
|
459
463
|
.cait-alignfiles{display:flex;gap:8px;flex-wrap:wrap}
|
|
460
464
|
.cait-alignfile{display:flex;align-items:center;gap:8px;padding:7px 9px;border:1px dashed var(--dsw-alias-border-l1);border-radius:8px;background:var(--dsw-alias-bg-layer-1);min-width:0;flex:1 1 300px}
|
|
461
465
|
.cait-alignlabel{font-size:10px;letter-spacing:.04em;text-transform:uppercase;color:var(--dsw-alias-label-secondary);flex:0 0 auto}
|