Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"react-markdown": "^10.1.0",
"react-router-dom": "^7.13.0",
"remark-gfm": "^4.0.1",
"sweetalert2": "^11.26.24",
"typescript": "^5.9.3",
"zustand": "^5.0.11"
},
Expand Down
36 changes: 29 additions & 7 deletions public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,16 @@
"via": "Via ",
"recordNewTrip": "Record New Trip",
"importSuica": "Import Suica CSV",
"deleteConfirm": "Confirm delete?",
"deleteConfirm": "确认删除?",
"parseSuccess": "Successfully parsed {count} new trips. Import?{{skipMsg}}\n(Press F12 to see matching logs)",
"skipMsg": "\n(Skipped {{count}} duplicate records)",
"importSuccess": "Imported {count} trips!{{skipMsg}}",
"skipMsgShort": " (Skipped {{count}} duplicates)",
"allExist": "Parsing complete, but all records ({{count}}) already exist. No need to import.",
"noImport": "No importable trips found, or parsing failed.",
"readError": "Error reading or parsing file."
"readError": "Error reading or parsing file.",
"parsingSuica": "解析 Suica CSV 数据...",
"syncFail": "云端同步失败"
},
"search": {
"close": "Close",
Expand Down Expand Up @@ -134,9 +136,10 @@
"createBtn": "Create Account"
},
"folder": {
"saveFail": "Save failed: ",
"saveFail": "保存失败: ",
"title": "Folders",
"placeholder": "Name..."
"placeholder": "Name...",
"deleteConfirm": "Delete this folder?"
},
"tripEdit": {
"editTitle": "Edit Trip",
Expand Down Expand Up @@ -167,7 +170,8 @@
"fillInfo": "Please complete the info",
"autoMaxLimit": "Auto route exceeds 6 transfers or has no solution.\nContinue deep search? (This may take a while)",
"autoFail": "Failed to route: ",
"pathTooLong": "Path too long"
"pathTooLong": "Path too long",
"saveFail": "云端保存失败: "
},
"app": {
"fetchRemote": "Fetching remote data... (10%)",
Expand Down Expand Up @@ -199,6 +203,24 @@
"parseFail": "Parsing failed",
"fileSkip": "File {{name}} parsing failed, skipped",
"importCount": "Successfully imported {{count}} files!",
"fileProcErr": "Unknown error occurred during file processing"
"fileProcErr": "Unknown error occurred during file processing",
"mergeDataConfirm": "检测到本地有数据或个人配置,是否保留并与云端数据合并?\n\n点击【确定】合并 (Keep Local)\n点击【取消】仅使用云端数据 (Overwrite Local)"
},
"confirm": {
"yes": "Yes",
"no": "No",
"ok": "OK"
},
"map": {
"loadFail": "地图加载失败 ({{domain}})\n请检查网络或切换 VPN 节点",
"rubberBand": "哎呀!橡皮筋拉断了,弹到手了!💥"
},
"chest": {
"searchingRoute": "正在搜索路线...",
"routeFound": "找到了路线",
"routeNotFound": "未找到路线"
},
"pin": {
"deleteConfirm": "删除?"
}
}
}
36 changes: 29 additions & 7 deletions public/locales/ja-JP/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,16 @@
"via": "経由 ",
"recordNewTrip": "新しい旅を記録",
"importSuica": "Suica CSVのインポート",
"deleteConfirm": "削除しますか?",
"deleteConfirm": "确认删除?",
"parseSuccess": "{count}件の新しい旅の解析に成功しました。インポートしますか?{{skipMsg}}\n(F12で詳細なマッチングログを確認できます)",
"skipMsg": "\n({{count}}件の重複記録をスキップしました)",
"importSuccess": "{count}件の旅をインポートしました!{{skipMsg}}",
"skipMsgShort": " ({{count}}件の重複をスキップ)",
"allExist": "解析完了。すべての記録({{count}}件)は既に存在するため、インポートは不要です。",
"noImport": "インポート可能な旅が見つからないか、解析に失敗しました。",
"readError": "ファイルの読み込みまたは解析でエラーが発生しました。"
"readError": "ファイルの読み込みまたは解析でエラーが発生しました。",
"parsingSuica": "解析 Suica CSV 数据...",
"syncFail": "云端同步失败"
},
"search": {
"close": "閉じる",
Expand Down Expand Up @@ -134,9 +136,10 @@
"createBtn": "アカウント作成"
},
"folder": {
"saveFail": "保存に失敗しました: ",
"saveFail": "保存失败: ",
"title": "フォルダ",
"placeholder": "名前..."
"placeholder": "名前...",
"deleteConfirm": "Delete this folder?"
},
"tripEdit": {
"editTitle": "旅程の編集",
Expand Down Expand Up @@ -167,7 +170,8 @@
"fillInfo": "情報を入力してください",
"autoMaxLimit": "自動検索が6回の乗換制限を超えるか、解がありません。\n制限なしのディープサーチを続けますか? (時間がかかる場合があります)",
"autoFail": "検索失敗: ",
"pathTooLong": "ルートが長すぎます"
"pathTooLong": "ルートが長すぎます",
"saveFail": "云端保存失败: "
},
"app": {
"fetchRemote": "リモートデータを取得中... (10%)",
Expand Down Expand Up @@ -199,6 +203,24 @@
"parseFail": "解析に失敗しました",
"fileSkip": "ファイル {{name}} の解析に失敗しました。スキップします",
"importCount": "{{count}}個のファイルをインポートしました!",
"fileProcErr": "ファイル処理中に不明なエラーが発生しました"
"fileProcErr": "ファイル処理中に不明なエラーが発生しました",
"mergeDataConfirm": "检测到本地有数据或个人配置,是否保留并与云端数据合并?\n\n点击【确定】合并 (Keep Local)\n点击【取消】仅使用云端数据 (Overwrite Local)"
},
"confirm": {
"yes": "はい",
"no": "いいえ",
"ok": "OK"
},
"map": {
"loadFail": "地图加载失败 ({{domain}})\n请检查网络或切换 VPN 节点",
"rubberBand": "哎呀!橡皮筋拉断了,弹到手了!💥"
},
"chest": {
"searchingRoute": "正在搜索路线...",
"routeFound": "找到了路线",
"routeNotFound": "未找到路线"
},
"pin": {
"deleteConfirm": "删除?"
}
}
}
32 changes: 27 additions & 5 deletions public/locales/zh-CN/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,9 @@
"skipMsgShort": " (跳过 {{count}} 重复)",
"allExist": "解析完成,但所有记录({{count}}条)已存在,无需重复导入。",
"noImport": "未找到可导入的行程,或者解析失败。",
"readError": "读取或解析文件出错。"
"readError": "读取或解析文件出错。",
"parsingSuica": "解析 Suica CSV 数据...",
"syncFail": "云端同步失败"
},
"search": {
"close": "关闭",
Expand Down Expand Up @@ -136,7 +138,8 @@
"folder": {
"saveFail": "保存失败: ",
"title": "收藏夹",
"placeholder": "名称..."
"placeholder": "名称...",
"deleteConfirm": "Delete this folder?"
},
"tripEdit": {
"editTitle": "编辑行程",
Expand Down Expand Up @@ -167,7 +170,8 @@
"fillInfo": "请完善信息",
"autoMaxLimit": "自动规划超出6次换乘限制或无解。\n是否继续无限制深度搜索?(这可能需要较长等待时间)",
"autoFail": "无法规划: ",
"pathTooLong": "路径过长"
"pathTooLong": "路径过长",
"saveFail": "云端保存失败: "
},
"app": {
"fetchRemote": "正在获取远端数据... (10%)",
Expand Down Expand Up @@ -199,6 +203,24 @@
"parseFail": "解析失败",
"fileSkip": "文件 {{name}} 解析失败,已跳过",
"importCount": "成功导入 {{count}} 个文件!",
"fileProcErr": "文件处理过程中发生未知错误"
"fileProcErr": "文件处理过程中发生未知错误",
"mergeDataConfirm": "检测到本地有数据或个人配置,是否保留并与云端数据合并?\n\n点击【确定】合并 (Keep Local)\n点击【取消】仅使用云端数据 (Overwrite Local)"
},
"confirm": {
"yes": "确定",
"no": "取消",
"ok": "好的"
},
"map": {
"loadFail": "地图加载失败 ({{domain}})\n请检查网络或切换 VPN 节点",
"rubberBand": "哎呀!橡皮筋拉断了,弹到手了!💥"
},
"chest": {
"searchingRoute": "正在搜索路线...",
"routeFound": "找到了路线",
"routeNotFound": "未找到路线"
},
"pin": {
"deleteConfirm": "删除?"
}
}
}
36 changes: 29 additions & 7 deletions public/locales/zh-TW/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,16 @@
"via": "經由 ",
"recordNewTrip": "記錄新行程",
"importSuica": "匯入 Suica CSV",
"deleteConfirm": "確認刪除?",
"deleteConfirm": "确认删除?",
"parseSuccess": "成功解析 {{count}} 條新行程。是否匯入?{{skipMsg}}\n(按 F12 打開主控台查看詳細比對記錄)",
"skipMsg": "\n(已跳過 {{count}} 條重複記錄)",
"importSuccess": "匯入了 {{count}} 條行程!{{skipMsg}}",
"skipMsgShort": " (跳過 {{count}} 重複)",
"allExist": "解析完成,但所有記錄({{count}}條)已存在,無需重複匯入。",
"noImport": "未找到可匯入的行程,或者解析失敗。",
"readError": "讀取或解析檔案出錯。"
"readError": "讀取或解析檔案出錯。",
"parsingSuica": "解析 Suica CSV 数据...",
"syncFail": "云端同步失败"
},
"search": {
"close": "關閉",
Expand Down Expand Up @@ -135,9 +137,10 @@
"createBtn": "建立帳號"
},
"folder": {
"saveFail": "保存失敗: ",
"saveFail": "保存失败: ",
"title": "收藏夾",
"placeholder": "名稱..."
"placeholder": "名稱...",
"deleteConfirm": "Delete this folder?"
},
"tripEdit": {
"editTitle": "編輯行程",
Expand Down Expand Up @@ -168,7 +171,8 @@
"fillInfo": "請完善信息",
"autoMaxLimit": "自動規劃超出6次換乘限制或無解。\n是否繼續無限制深度搜索?(這可能需要較長等待時間)",
"autoFail": "無法規劃: ",
"pathTooLong": "路徑過長"
"pathTooLong": "路徑過長",
"saveFail": "云端保存失败: "
},
"app": {
"fetchRemote": "正在取得遠端資料... (10%)",
Expand Down Expand Up @@ -200,6 +204,24 @@
"parseFail": "解析失敗",
"fileSkip": "檔案 {{name}} 解析失敗,已跳過",
"importCount": "成功匯入 {{count}} 個檔案!",
"fileProcErr": "檔案處理過程中發生未知錯誤"
"fileProcErr": "檔案處理過程中發生未知錯誤",
"mergeDataConfirm": "检测到本地有数据或个人配置,是否保留并与云端数据合并?\n\n点击【确定】合并 (Keep Local)\n点击【取消】仅使用云端数据 (Overwrite Local)"
},
"confirm": {
"yes": "確定",
"no": "取消",
"ok": "好的"
},
"map": {
"loadFail": "地图加载失败 ({{domain}})\n请检查网络或切换 VPN 节点",
"rubberBand": "哎呀!橡皮筋拉断了,弹到手了!💥"
},
"chest": {
"searchingRoute": "正在搜索路线...",
"routeFound": "找到了路线",
"routeNotFound": "未找到路线"
},
"pin": {
"deleteConfirm": "删除?"
}
}
}
9 changes: 5 additions & 4 deletions src/AppLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { showConfirm } from './utils/confirm';
import React, { useState, useRef, useEffect } from 'react';
import { DragProvider } from './components/DragContext';
import Chest from './components/Chest';
Expand Down Expand Up @@ -981,13 +982,13 @@ export const AppLayout: React.FC = () => {
const file = event.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = (e: any) => {
reader.onload = async (e: any) => {
try {
const backup = JSON.parse(e.target.result);
if (!backup.meta || (backup.meta.appName !== "RailLOOP" && backup.meta.appName !== "")) { alert(t("app.invalidBackup", "无效的备份文件")); return; }
const missingLines: string[] = [];
if (backup.dependencies && backup.dependencies.lines) { backup.dependencies.lines.forEach((lineKey: string) => { if (!railwayData[lineKey]) missingLines.push(lineKey); }); }
if (missingLines.length > 0) { const msg = t("app.missLine", "检测到缺少以下线路的基础数据,可能会导致显示异常:\n\n{{lines}}\n\n建议先去地图页面上传对应的 GeoJSON 文件。是否继续导入?", { lines: missingLines.slice(0, 5).join(", ") + (missingLines.length > 5 ? '...' : '') }); if (!confirm(msg)) return; }
if (missingLines.length > 0) { const msg = t("app.missLine", "检测到缺少以下线路的基础数据,可能会导致显示异常:\n\n{{lines}}\n\n建议先去地图页面上传对应的 GeoJSON 文件。是否继续导入?", { lines: missingLines.slice(0, 5).join(", ") + (missingLines.length > 5 ? '...' : '') }); if (!await showConfirm(msg)) return; }
const currentTripIds = new Set(trips.map(t => t.id));
const incomingTrips = backup.data.trips || [];
const uniqueIncomingTrips: any[] = [];
Expand Down Expand Up @@ -1019,7 +1020,7 @@ export const AppLayout: React.FC = () => {
const file = event.target.files[0];
if(!file) return;
const reader = new FileReader();
reader.onload = (e: any) => { try { const json = JSON.parse(e.target.result); applyCompanyData(json, { silent: false }); } catch(err) { alert(t("app.parseFail", "解析失败")); } };
reader.onload = async (e: any) => { try { const json = JSON.parse(e.target.result); applyCompanyData(json, { silent: false }); } catch(err) { alert(t("app.parseFail", "解析失败")); } };
reader.readAsText(file);
event.target.value = '';
};
Expand All @@ -1030,7 +1031,7 @@ export const AppLayout: React.FC = () => {
const readTasks = Array.from(files).map((file: any) => {
return new Promise((resolve) => {
const reader = new FileReader();
reader.onload = (e: any) => {
reader.onload = async (e: any) => {
try {
const json = JSON.parse(e.target.result);
const companyName = file.name.replace(/\.(geojson|json)$/i, "");
Expand Down
Loading