From cb24e2fd1fab1924084f2765bb870f01570544a2 Mon Sep 17 00:00:00 2001 From: Yasutake Yohei <61961825+yasutakeyohei@users.noreply.github.com> Date: Tue, 27 Oct 2026 15:21:44 +0900 Subject: park-editor: 文字化けを保存時に警告し、場所を表示 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 保存を中止せず、該当セルを赤くハイライトして、保存前に公園名・項目・値の一覧を確認できるようにした。サーバー側も保存を継続し、警告を返す。 --- scripts/park-editor.mjs | 45 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) (limited to 'scripts/park-editor.mjs') diff --git a/scripts/park-editor.mjs b/scripts/park-editor.mjs index d0c08da..e3bae45 100644 --- a/scripts/park-editor.mjs +++ b/scripts/park-editor.mjs @@ -51,6 +51,38 @@ function datasetFromUrl(url) { return DATASETS[match[1]] ? { id: match[1], dataset: DATASETS[match[1]] } : null; } +// 文字化け(U+FFFD)の場所を「レコード名 / 項目名」で返す。 +// 保存は止めず、どこを直せばよいか分かるようにするための情報。 +const NAME_KEYS = ["公園名", "name", "名称", "our_name"]; +function recordLabel(obj) { + const sources = []; + if (obj && obj.properties) sources.push(obj.properties); + sources.push(obj); + for (const source of sources) { + for (const key of NAME_KEYS) { + const value = source && source[key]; + if (typeof value === "string" && value) return value; + } + } + return ""; +} +function findCorruptions(value, path, out) { + if (typeof value === "string") { + if (value.includes("\uFFFD")) out.push({ where: path, value }); + } else if (Array.isArray(value)) { + value.forEach((item, index) => { + const label = recordLabel(item); + findCorruptions(item, label || `${path}[${index + 1}]`, out); + }); + } else if (value && typeof value === "object") { + for (const [key, child] of Object.entries(value)) { + if (key.includes("\uFFFD")) out.push({ where: `${path} / ${key}(項目名)`, value: "" }); + findCorruptions(child, path ? `${path} / ${key}` : key, out); + } + } + return out; +} + const server = createServer(async (req, res) => { try { const url = (req.url || "").split("?")[0]; @@ -104,9 +136,14 @@ const server = createServer(async (req, res) => { ? `${JSON.stringify(data, null, 2)}\n` : `${JSON.stringify(data)}\n`; if (text.includes("\uFFFD")) { - throw new Error( - "文字化け(U+FFFD)が含まれているため保存を中止しました。入力内容を確認してください。", - ); + // 保存は中止せず、どこが文字化けしているかを返す(入力内容を失わないため) + console.warn("文字化け(U+FFFD)を含むデータを保存しました"); + } + const warnings = findCorruptions(data, "", []).slice(0, 50); + if (warnings.length) { + for (const warning of warnings.slice(0, 10)) { + console.warn(` 文字化け: ${warning.where} = ${warning.value}`); + } } const tmp = `${target.dataset.path}.tmp`; await writeFile(tmp, text, "utf-8"); @@ -118,7 +155,7 @@ const server = createServer(async (req, res) => { res, 200, "application/json; charset=utf-8", - JSON.stringify({ ok: true, count }), + JSON.stringify({ ok: true, count, warnings }), ); return; } -- cgit v1.3.1