diff --git a/package.json b/package.json
index 6e9834a..83d61d5 100644
--- a/package.json
+++ b/package.json
@@ -29,6 +29,7 @@
"@ungap/structured-clone": "^1.2.0",
"@vicons/fluent": "^0.12.0",
"@vue/tsconfig": "^0.4.0",
+ "codemirror": "^6.0.1",
"fflate": "^0.8.0",
"jieba-wasm": "^0.0.2",
"jss": "^10.10.0",
@@ -41,6 +42,7 @@
"sass": "^1.64.1",
"save-file": "^2.3.1",
"vue": "^3.3.4",
+ "vue-codemirror": "^6.1.1",
"vue-i18n": "9",
"vue-virtual-scroller": "next",
"vuedraggable": "next"
diff --git a/src/App.vue b/src/App.vue
index 31001b4..a8cb111 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -48,6 +48,7 @@
+
@@ -74,6 +75,7 @@ import ContextMenu from "./components/ContextMenu.vue";
import UploadDBDialog from "./components/modals/UploadDBDialog.vue";
import SplitWordModal from "./components/modals/SplitWordModal.vue";
import ServiceWorkerUpdater from "./components/ServiceWorkerUpdater.vue";
+import ImportPlainTextModal from "./components/modals/ImportPlainTextModal.vue";
const LyricEditor = defineAsyncComponent(() => import("./components/LyricEditor.vue"));
const LyricSyncEditor = defineAsyncComponent(() => import("./components/LyricSyncEditor.vue"));
const AMLLPreviewView = defineAsyncComponent(() => import("./components/AMLLPreviewView.vue"));
diff --git a/src/components/LyricLineEditor.vue b/src/components/LyricLineEditor.vue
index 7e8d9dd..a65727a 100644
--- a/src/components/LyricLineEditor.vue
+++ b/src/components/LyricLineEditor.vue
@@ -17,15 +17,18 @@
-
-
-
@@ -64,7 +67,7 @@ const editState = reactive({
romanLine: props.line.romanLyric,
});
const settings = useSettings();
-const { t } = useI18n();
+const { t } = useI18n({ useScope: "global" });
const inputRef = ref(null);
function onSort(e: CustomEvent & {
diff --git a/src/components/TopBar.vue b/src/components/TopBar.vue
index 6f8f635..7614d9b 100644
--- a/src/components/TopBar.vue
+++ b/src/components/TopBar.vue
@@ -92,7 +92,7 @@ const settings = useSettings();
const aboutModalOpened = ref(false);
const notify = useNotification();
const dialogs = useDialogs();
-const { t } = useI18n();
+const { t } = useI18n({ useScope: "global" });
const MENU = ref({
file: [
@@ -104,6 +104,8 @@ const MENU = ref({
// { type: 'divider' },
{
label: t('topBar.menu.importLyric'), key: 'import-from', children: [{
+ label: t('topBar.menu.importLyricFromText'), key: 'import-from-text',
+ }, {
label: t('topBar.menu.importLyricFromLrc'), key: 'import-from-lrc',
}, {
label: t('topBar.menu.importLyricFromYrc'), key: 'import-from-yrc',
@@ -187,6 +189,10 @@ function onSelectMenu(key: string) {
fileDialog.remove();
break;
}
+ case "import-from-text": {
+ dialogs.importFromText = true;
+ break;
+ }
case "import-from-lrc": {
const fileDialog = document.createElement("input");
fileDialog.type = "file";
diff --git a/src/components/modals/ImportPlainTextModal.vue b/src/components/modals/ImportPlainTextModal.vue
new file mode 100644
index 0000000..d5c3851
--- /dev/null
+++ b/src/components/modals/ImportPlainTextModal.vue
@@ -0,0 +1,223 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/modals/ProgressOverlay.vue b/src/components/modals/ProgressOverlay.vue
index d447aa0..a8d7db1 100644
--- a/src/components/modals/ProgressOverlay.vue
+++ b/src/components/modals/ProgressOverlay.vue
@@ -20,6 +20,6 @@ import { storeToRefs } from "pinia";
import { useI18n } from "vue-i18n";
const { currentProgresses } = storeToRefs(useProgress());
-const { t } = useI18n();
+const { t } = useI18n({ useScope: "global" });
diff --git a/src/components/modals/SplitWordModal.vue b/src/components/modals/SplitWordModal.vue
index ac5a575..fc0112a 100644
--- a/src/components/modals/SplitWordModal.vue
+++ b/src/components/modals/SplitWordModal.vue
@@ -20,7 +20,7 @@ import { useI18n } from "vue-i18n";
const lyric = useEditingLyric();
const notify = useNotification();
const dialogs = useDialogs();
-const { t } = useI18n();
+const { t } = useI18n({ useScope: "global" });
const submitData = reactive({
name: "",
diff --git a/src/components/modals/TutorialModal.vue b/src/components/modals/TutorialModal.vue
index 4082722..f5551dc 100644
--- a/src/components/modals/TutorialModal.vue
+++ b/src/components/modals/TutorialModal.vue
@@ -167,6 +167,6 @@ import { useSettings } from '../../store';
import { useI18n } from "vue-i18n";
const settings = useSettings();
-const { t } = useI18n();
+const { t } = useI18n({ useScope: "global" });
diff --git a/src/components/modals/UploadDBDialog.vue b/src/components/modals/UploadDBDialog.vue
index e737f5c..75af3ad 100644
--- a/src/components/modals/UploadDBDialog.vue
+++ b/src/components/modals/UploadDBDialog.vue
@@ -57,7 +57,7 @@ import { useI18n } from "vue-i18n";
const lyric = useEditingLyric();
const notify = useNotification();
const dialogs = useDialogs();
-const { t } = useI18n();
+const { t } = useI18n({ useScope: "global" });
const submitData = reactive({
name: "",
diff --git a/src/i18n/index.ts b/src/i18n/index.ts
index 4912571..6f2479d 100644
--- a/src/i18n/index.ts
+++ b/src/i18n/index.ts
@@ -1,5 +1,4 @@
import { createI18n } from "vue-i18n";
-import type { DefineLocaleMessage } from "vue-i18n";
import { zhCN } from "./zh-cn";
import { enUS } from "./en-us";
@@ -18,6 +17,7 @@ export type LocateMessage = FullPartial;
export const i18n = createI18n<[typeof zhCN]>({
legacy: false,
globalInjection: true,
+ silentFallbackWarn: true,
locale: navigator.language,
fallbackLocale: [...navigator.languages, "zh-CN"],
messages: {
diff --git a/src/i18n/zh-cn.ts b/src/i18n/zh-cn.ts
index f91589d..e5209e4 100644
--- a/src/i18n/zh-cn.ts
+++ b/src/i18n/zh-cn.ts
@@ -11,6 +11,7 @@ export const zhCN = {
openLyric: "打开歌词",
saveLyric: "保存歌词",
importLyric: "导入歌词...",
+ importLyricFromText: "从纯文本导入",
importLyricFromLrc: "从 LRC 歌词导入",
importLyricFromYrc: "从 YRC 歌词导入",
importLyricFromQrc: "从 QRC 歌词导入",
@@ -106,6 +107,23 @@ export const zhCN = {
title: "拆分单词",
splitBtn: "拆分",
},
+ importPlainTextModal: {
+ title: "从纯文本导入歌词",
+ textPlaceholder: "纯文本歌词内容",
+ importMode: "导入模式",
+ lyricOnly: "仅歌词",
+ lyricWithTranslation: "歌词和翻译歌词",
+ lyricWithTranslationAndRoman: "歌词和翻译、音译歌词",
+ lyricSplitMode: "歌词分行(翻译和音译)模式",
+ sameLineWithSeparator: "同行分隔",
+ sameLineSeparator: "歌词行分隔符",
+ sameLineSeparatorPlaceholder: "留空则不分隔",
+ interleavedLine: "多行交错分隔",
+ swapTransAndRoman: "交换翻译行和音译行",
+ wordSeparator: "单词分隔符",
+ wordSeparatorPlaceholder: "留空则不分隔",
+ importBtn: "导入歌词",
+ },
uploadDBDialog: {
title: "提交歌词到 AMLL 歌词数据库(仅网易云)",
ncmOnlyWarning:
@@ -158,7 +176,7 @@ export const zhCN = {
title: "加载歌词",
content: [
"您可以通过左上角的菜单 - 文件 - 打开歌词来加载歌词哦!",
- "如果您的歌词不是 TTML 格式也没有关系,歌词工具支持从 LRC/YRC/QRC/Lyricify Syllable 格式导入歌词哦!",
+ "如果您的歌词不是 TTML 格式也没有关系,歌词工具支持从 纯文本/LRC/YRC/QRC/Lyricify Syllable 格式导入歌词哦!",
].join("\n"),
},
step2: {
diff --git a/src/store/dialogs.ts b/src/store/dialogs.ts
index 4d1653d..e765d0c 100644
--- a/src/store/dialogs.ts
+++ b/src/store/dialogs.ts
@@ -4,5 +4,6 @@ export const useDialogs = defineStore("dialogs", {
state: () => ({
submitLyric: false,
editLyricInfo: false,
+ importFromText: false,
}),
});
diff --git a/yarn.lock b/yarn.lock
index f28def8..a514994 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1398,6 +1398,89 @@ __metadata:
languageName: node
linkType: hard
+"@codemirror/autocomplete@npm:^6.0.0":
+ version: 6.9.0
+ resolution: "@codemirror/autocomplete@npm:6.9.0"
+ dependencies:
+ "@codemirror/language": ^6.0.0
+ "@codemirror/state": ^6.0.0
+ "@codemirror/view": ^6.6.0
+ "@lezer/common": ^1.0.0
+ peerDependencies:
+ "@codemirror/language": ^6.0.0
+ "@codemirror/state": ^6.0.0
+ "@codemirror/view": ^6.0.0
+ "@lezer/common": ^1.0.0
+ checksum: a5f661944c75f40b02c90a193c9a459c0fd7e335c0ac5973420c19157dfb46010f573c2b70731591fe477e7a2ad10121ff3ae394a72d450946d7b886c28b0368
+ languageName: node
+ linkType: hard
+
+"@codemirror/commands@npm:6.x, @codemirror/commands@npm:^6.0.0":
+ version: 6.2.4
+ resolution: "@codemirror/commands@npm:6.2.4"
+ dependencies:
+ "@codemirror/language": ^6.0.0
+ "@codemirror/state": ^6.2.0
+ "@codemirror/view": ^6.0.0
+ "@lezer/common": ^1.0.0
+ checksum: 468895fa19ff0554181b698c81f850820de5c0289cab92c44392fb127286f09ca72b921d6ea4353b70b616a4fd0c3667d86b6f917202a3ad2e196eb7b581f7b6
+ languageName: node
+ linkType: hard
+
+"@codemirror/language@npm:6.x, @codemirror/language@npm:^6.0.0":
+ version: 6.9.0
+ resolution: "@codemirror/language@npm:6.9.0"
+ dependencies:
+ "@codemirror/state": ^6.0.0
+ "@codemirror/view": ^6.0.0
+ "@lezer/common": ^1.0.0
+ "@lezer/highlight": ^1.0.0
+ "@lezer/lr": ^1.0.0
+ style-mod: ^4.0.0
+ checksum: 9a897fb0f569159eeafb7dce83061b425af7244bbeae2649e0e677488548b2a02eaf0c13c0c5b4d59da55e8866e6f4dc7abe3dfaa09c13749a2fa2c0dbc0c565
+ languageName: node
+ linkType: hard
+
+"@codemirror/lint@npm:^6.0.0":
+ version: 6.4.0
+ resolution: "@codemirror/lint@npm:6.4.0"
+ dependencies:
+ "@codemirror/state": ^6.0.0
+ "@codemirror/view": ^6.0.0
+ crelt: ^1.0.5
+ checksum: ba15f7dd87afbceafaa0b68f94b0d53727e4aacca7a81a4ed3278706df5787fdf18cd3f0d807a136f902b2fc2296bf3490462fd543d1d4ced17a0d8c171820fd
+ languageName: node
+ linkType: hard
+
+"@codemirror/search@npm:^6.0.0":
+ version: 6.5.1
+ resolution: "@codemirror/search@npm:6.5.1"
+ dependencies:
+ "@codemirror/state": ^6.0.0
+ "@codemirror/view": ^6.0.0
+ crelt: ^1.0.5
+ checksum: 672515c20238f69ff5cd8b662128699178ba7e020fc44a8ed2b0dcc25d8d5f5579418865616dd8809317a408fb08b6001a442f0fb706a772250b4284d7437045
+ languageName: node
+ linkType: hard
+
+"@codemirror/state@npm:6.x, @codemirror/state@npm:^6.0.0, @codemirror/state@npm:^6.1.4, @codemirror/state@npm:^6.2.0":
+ version: 6.2.1
+ resolution: "@codemirror/state@npm:6.2.1"
+ checksum: d12a321d0471b264b9d3259042bff913a8b939e8d28d408ff452004538a71ca9d5329df3f8a1d8a9183f5b42a7ef5b200737bcab1065714f5ae8e0a5ba9d59d3
+ languageName: node
+ linkType: hard
+
+"@codemirror/view@npm:6.x, @codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.6.0":
+ version: 6.16.0
+ resolution: "@codemirror/view@npm:6.16.0"
+ dependencies:
+ "@codemirror/state": ^6.1.4
+ style-mod: ^4.0.0
+ w3c-keyname: ^2.2.4
+ checksum: 54d412b5159716c8a1a9c46fa04ff083e68a663cb887e6e2a4ca86fe9c3930d5255200fe84c65620e0a442f62dc2c13df277bcd1d4eef2e11e3c4e124fcf9d38
+ languageName: node
+ linkType: hard
+
"@css-render/plugin-bem@npm:^0.15.10":
version: 0.15.12
resolution: "@css-render/plugin-bem@npm:0.15.12"
@@ -1705,6 +1788,31 @@ __metadata:
languageName: node
linkType: hard
+"@lezer/common@npm:^1.0.0":
+ version: 1.0.4
+ resolution: "@lezer/common@npm:1.0.4"
+ checksum: 0bea82da76e0b89afad4e5159d3add460022916352c47906ec67b26d6fe5ec9cb8e23df0e2bf0adef765ae78bed1706fc573a11506d01a80112a5b6dd317730c
+ languageName: node
+ linkType: hard
+
+"@lezer/highlight@npm:^1.0.0":
+ version: 1.1.6
+ resolution: "@lezer/highlight@npm:1.1.6"
+ dependencies:
+ "@lezer/common": ^1.0.0
+ checksum: 411a702394c4c996b7d7f145a38f3a85a8cc698b3918acc7121c629255bb76d4ab383753f69009e011dc415210c6acbbb5b27bde613259ab67e600b29397b03b
+ languageName: node
+ linkType: hard
+
+"@lezer/lr@npm:^1.0.0":
+ version: 1.3.10
+ resolution: "@lezer/lr@npm:1.3.10"
+ dependencies:
+ "@lezer/common": ^1.0.0
+ checksum: 9d3c22bf692561cf7fe2f3d14e821913f87116ff9d73b8b550e7998b6135baae9f504563846a4257e1bb4eae97ae1b60c06c6066450ddeef5e03e8783526b2ae
+ languageName: node
+ linkType: hard
+
"@nodelib/fs.scandir@npm:2.1.5":
version: 2.1.5
resolution: "@nodelib/fs.scandir@npm:2.1.5"
@@ -2747,6 +2855,7 @@ __metadata:
"@vicons/fluent": ^0.12.0
"@vitejs/plugin-vue": ^4.2.3
"@vue/tsconfig": ^0.4.0
+ codemirror: ^6.0.1
fflate: ^0.8.0
jieba-wasm: ^0.0.2
jss: ^10.10.0
@@ -2766,6 +2875,7 @@ __metadata:
vite-plugin-wasm: ^3.2.2
vite-svg-loader: ^4.0.0
vue: ^3.3.4
+ vue-codemirror: ^6.1.1
vue-i18n: 9
vue-virtual-scroller: next
vuedraggable: next
@@ -3994,6 +4104,21 @@ __metadata:
languageName: node
linkType: hard
+"codemirror@npm:^6.0.1":
+ version: 6.0.1
+ resolution: "codemirror@npm:6.0.1"
+ dependencies:
+ "@codemirror/autocomplete": ^6.0.0
+ "@codemirror/commands": ^6.0.0
+ "@codemirror/language": ^6.0.0
+ "@codemirror/lint": ^6.0.0
+ "@codemirror/search": ^6.0.0
+ "@codemirror/state": ^6.0.0
+ "@codemirror/view": ^6.0.0
+ checksum: 1a78f7077ac5801bdbff162aa0c61bf2b974603c7e9a477198c3ce50c789af674a061d7c293c58b73807eda345c2b5228c38ad2aabb9319d552d5486f785cbef
+ languageName: node
+ linkType: hard
+
"collection-visit@npm:^1.0.0":
version: 1.0.0
resolution: "collection-visit@npm:1.0.0"
@@ -4459,6 +4584,13 @@ __metadata:
languageName: node
linkType: hard
+"crelt@npm:^1.0.5":
+ version: 1.0.6
+ resolution: "crelt@npm:1.0.6"
+ checksum: dad842093371ad702afbc0531bfca2b0a8dd920b23a42f26e66dabbed9aad9acd5b9030496359545ef3937c3aced0fd4ac39f7a2d280a23ddf9eb7fdcb94a69f
+ languageName: node
+ linkType: hard
+
"cross-spawn@npm:^5.0.1":
version: 5.1.0
resolution: "cross-spawn@npm:5.1.0"
@@ -12541,6 +12673,13 @@ __metadata:
languageName: node
linkType: hard
+"style-mod@npm:^4.0.0":
+ version: 4.1.0
+ resolution: "style-mod@npm:4.1.0"
+ checksum: 8402b14ca11113a3640d46b3cf7ba49f05452df7846bc5185a3535d9b6a64a3019e7fb636b59ccbb7816aeb0725b24723e77a85b05612a9360e419958e13b4e6
+ languageName: node
+ linkType: hard
+
"subarg@npm:^1.0.0":
version: 1.0.0
resolution: "subarg@npm:1.0.0"
@@ -13598,6 +13737,21 @@ __metadata:
languageName: node
linkType: hard
+"vue-codemirror@npm:^6.1.1":
+ version: 6.1.1
+ resolution: "vue-codemirror@npm:6.1.1"
+ dependencies:
+ "@codemirror/commands": 6.x
+ "@codemirror/language": 6.x
+ "@codemirror/state": 6.x
+ "@codemirror/view": 6.x
+ peerDependencies:
+ codemirror: 6.x
+ vue: 3.x
+ checksum: 90eb7a786040a352e9e5b21c9db582dab60dff1289e26470cc8c51613c4457ca1b85d72b8042d2c059b642b9526e77532751847a6911453eb403762d3eaf0799
+ languageName: node
+ linkType: hard
+
"vue-demi@npm:>=0.14.5":
version: 0.14.5
resolution: "vue-demi@npm:0.14.5"
@@ -13700,6 +13854,13 @@ __metadata:
languageName: node
linkType: hard
+"w3c-keyname@npm:^2.2.4":
+ version: 2.2.8
+ resolution: "w3c-keyname@npm:2.2.8"
+ checksum: 95bafa4c04fa2f685a86ca1000069c1ec43ace1f8776c10f226a73296caeddd83f893db885c2c220ebeb6c52d424e3b54d7c0c1e963bbf204038ff1a944fbb07
+ languageName: node
+ linkType: hard
+
"walk@npm:2.3.3":
version: 2.3.3
resolution: "walk@npm:2.3.3"