const fs = require('fs'); const path = require('path'); const localesDir = path.join(__dirname, 'src', 'locales'); const translations = { 'zh': { "timestamp": { "pageTitle": "时间戳转换", "pageDescription": "Unix时间戳与日期时间快速互转", "pageCategory": "开发", "backToTools": "返回工具列表", "timezoneLabel": "时区选择", "tsToDate": "时间戳转日期", "dateToTs": "日期转时间戳", "tsInputPlaceholder": "请输入时间戳,如 1700000000", "tsEmptyHint": "输入时间戳后自动显示转换结果", "dateEmptyHint": "选择日期后自动显示时间戳", "getCurrentTime": "获取当前时间", "clear": "清空", "copied": "已复制到剪贴板", "result": { "localTime": "本地时间", "utcTime": "UTC 时间", "iso8601": "ISO 8601", "relative": "相对时间", "weekday": "星期", "secondsTs": "秒级时间戳", "msTs": "毫秒级时间戳", "isoString": "ISO 字符串", "invalidDate": "无效日期", "unknown": "未知" }, "timezone": { "local": "本地时间", "utc": "UTC", "shanghai": "Asia/Shanghai (北京)", "tokyo": "Asia/Tokyo (东京)", "newyork": "America/New_York (纽约)", "london": "Europe/London (伦敦)", "losangeles": "America/Los_Angeles (洛杉矶)" }, "badge": { "seconds": "秒级", "ms": "毫秒级" } } }, 'en': { "timestamp": { "pageTitle": "Timestamp Converter", "pageDescription": "Quick conversion between Unix timestamps and datetime", "pageCategory": "Dev", "backToTools": "Back to Tools", "timezoneLabel": "Timezone", "tsToDate": "Timestamp to Date", "dateToTs": "Date to Timestamp", "tsInputPlaceholder": "Enter a timestamp, e.g. 1700000000", "tsEmptyHint": "Conversion results will appear after entering a timestamp", "dateEmptyHint": "Timestamp will appear after selecting a date", "getCurrentTime": "Get Current Time", "clear": "Clear", "copied": "Copied to clipboard", "result": { "localTime": "Local Time", "utcTime": "UTC Time", "iso8601": "ISO 8601", "relative": "Relative Time", "weekday": "Weekday", "secondsTs": "Seconds Timestamp", "msTs": "Milliseconds Timestamp", "isoString": "ISO String", "invalidDate": "Invalid Date", "unknown": "Unknown" }, "timezone": { "local": "Local Time", "utc": "UTC", "shanghai": "Asia/Shanghai (Beijing)", "tokyo": "Asia/Tokyo (Tokyo)", "newyork": "America/New_York (New York)", "london": "Europe/London (London)", "losangeles": "America/Los_Angeles (Los Angeles)" }, "badge": { "seconds": "Seconds", "ms": "Milliseconds" } } }, 'ja': { "timestamp": { "pageTitle": "タイムスタンプ変換", "pageDescription": "Unixタイムスタンプと日時を素早く相互変換", "pageCategory": "開発", "backToTools": "ツール一覧に戻る", "timezoneLabel": "タイムゾーン", "tsToDate": "タイムスタンプ → 日付", "dateToTs": "日付 → タイムスタンプ", "tsInputPlaceholder": "タイムスタンプを入力してください(例: 1700000000)", "tsEmptyHint": "タイムスタンプを入力すると変換結果が表示されます", "dateEmptyHint": "日付を選択するとタイムスタンプが表示されます", "getCurrentTime": "現在の時刻を取得", "clear": "クリア", "copied": "クリップボードにコピーしました", "result": { "localTime": "ローカル時間", "utcTime": "UTC 時間", "iso8601": "ISO 8601", "relative": "相対時間", "weekday": "曜日", "secondsTs": "秒単位タイムスタンプ", "msTs": "ミリ秒単位タイムスタンプ", "isoString": "ISO 文字列", "invalidDate": "無効な日付", "unknown": "不明" }, "timezone": { "local": "ローカル時間", "utc": "UTC", "shanghai": "Asia/Shanghai (北京)", "tokyo": "Asia/Tokyo (東京)", "newyork": "America/New_York (ニューヨーク)", "london": "Europe/London (ロンドン)", "losangeles": "America/Los_Angeles (ロサンゼルス)" }, "badge": { "seconds": "秒単位", "ms": "ミリ秒単位" } } }, 'ko': { "timestamp": { "pageTitle": "타임스탬프 변환", "pageDescription": "Unix 타임스탬프와 날짜/시간을 빠르게 상호 변환", "pageCategory": "개발", "backToTools": "도구 목록으로 돌아가기", "timezoneLabel": "시간대", "tsToDate": "타임스탬프 → 날짜", "dateToTs": "날짜 → 타임스탬프", "tsInputPlaceholder": "타임스탬프를 입력하세요 (예: 1700000000)", "tsEmptyHint": "타임스탬프를 입력하면 변환 결과가 표시됩니다", "dateEmptyHint": "날짜를 선택하면 타임스탬프가 표시됩니다", "getCurrentTime": "현재 시간 가져오기", "clear": "지우기", "copied": "클립보드에 복사됨", "result": { "localTime": "로컬 시간", "utcTime": "UTC 시간", "iso8601": "ISO 8601", "relative": "상대 시간", "weekday": "요일", "secondsTs": "초 단위 타임스탬프", "msTs": "밀리초 단위 타임스탬프", "isoString": "ISO 문자열", "invalidDate": "유효하지 않은 날짜", "unknown": "알 수 없음" }, "timezone": { "local": "로컬 시간", "utc": "UTC", "shanghai": "Asia/Shanghai (베이징)", "tokyo": "Asia/Tokyo (도쿄)", "newyork": "America/New_York (뉴욕)", "london": "Europe/London (런던)", "losangeles": "America/Los_Angeles (로스앤젤레스)" }, "badge": { "seconds": "초 단위", "ms": "밀리초 단위" } } }, 'ru': { "timestamp": { "pageTitle": "Преобразование временных меток", "pageDescription": "Быстрая конвертация между Unix-временными метками и датой/временем", "pageCategory": "Разработка", "backToTools": "Назад к списку инструментов", "timezoneLabel": "Часовой пояс", "tsToDate": "Метка → Дата", "dateToTs": "Дата → Метка", "tsInputPlaceholder": "Введите временную метку, например 1700000000", "tsEmptyHint": "Результаты появятся после ввода временной метки", "dateEmptyHint": "Временная метка появится после выбора даты", "getCurrentTime": "Получить текущее время", "clear": "Очистить", "copied": "Скопировано в буфер обмена", "result": { "localTime": "Местное время", "utcTime": "Время UTC", "iso8601": "ISO 8601", "relative": "Относительное время", "weekday": "День недели", "secondsTs": "Метка в секундах", "msTs": "Метка в миллисекундах", "isoString": "ISO-строка", "invalidDate": "Недопустимая дата", "unknown": "Неизвестно" }, "timezone": { "local": "Местное время", "utc": "UTC", "shanghai": "Asia/Shanghai (Пекин)", "tokyo": "Asia/Tokyo (Токио)", "newyork": "America/New_York (Нью-Йорк)", "london": "Europe/London (Лондон)", "losangeles": "America/Los_Angeles (Лос-Анджелес)" }, "badge": { "seconds": "В секундах", "ms": "В миллисекундах" } } }, 'zh-TW': { "timestamp": { "pageTitle": "時間戳轉換", "pageDescription": "Unix時間戳與日期時間快速互轉", "pageCategory": "開發", "backToTools": "返回工具列表", "timezoneLabel": "時區選擇", "tsToDate": "時間戳轉日期", "dateToTs": "日期轉時間戳", "tsInputPlaceholder": "請輸入時間戳,如 1700000000", "tsEmptyHint": "輸入時間戳後自動顯示轉換結果", "dateEmptyHint": "選擇日期後自動顯示時間戳", "getCurrentTime": "取得目前時間", "clear": "清空", "copied": "已複製到剪貼簿", "result": { "localTime": "本地時間", "utcTime": "UTC 時間", "iso8601": "ISO 8601", "relative": "相對時間", "weekday": "星期", "secondsTs": "秒級時間戳", "msTs": "毫秒級時間戳", "isoString": "ISO 字串", "invalidDate": "無效日期", "unknown": "未知" }, "timezone": { "local": "本地時間", "utc": "UTC", "shanghai": "Asia/Shanghai (北京)", "tokyo": "Asia/Tokyo (東京)", "newyork": "America/New_York (紐約)", "london": "Europe/London (倫敦)", "losangeles": "America/Los_Angeles (洛杉磯)" }, "badge": { "seconds": "秒級", "ms": "毫秒級" } } } }; function formatNested(obj, indent) { const pad = ' '.repeat(indent); const innerPad = ' '.repeat(indent + 2); const keys = Object.keys(obj); let result = '{\n'; keys.forEach((key, idx) => { const val = obj[key]; if (typeof val === 'object' && val !== null) { result += innerPad + '"' + key + '": ' + formatNested(val, indent + 2); } else { const escaped = String(val).replace(/"/g, '\\"'); result += innerPad + '"' + key + '": "' + escaped + '"'; } if (idx < keys.length - 1) result += ','; result += '\n'; }); result += pad + '}'; return result; } function formatTimestampBlock(obj) { const pad2 = ' '; const pad4 = ' '; const keys = Object.keys(obj); let result = pad2 + '"timestamp": {\n'; keys.forEach((key, idx) => { const val = obj[key]; if (typeof val === 'object' && val !== null) { const subKeys = Object.keys(val); result += pad4 + '"' + key + '": {\n'; subKeys.forEach((sk, sIdx) => { const sv = val[sk]; result += ' "' + sk + '": "' + String(sv).replace(/"/g, '\\"') + '"'; if (sIdx < subKeys.length - 1) result += ','; result += '\n'; }); result += pad4 + '}'; } else { result += pad4 + '"' + key + '": "' + String(val).replace(/"/g, '\\"') + '"'; } if (idx < keys.length - 1) result += ','; result += '\n'; }); result += pad2 + '}'; return result; } // Find the LAST line containing the final `}` - we need to: // 1. Skip any closing } that closes a nested object inside an already-closed outer block // 2. The actual outer } has nothing but whitespace/comments after it. // Strategy: Find the position of last `}` at the end of file. // Since JSON can have nested closing braces, we use brace-counting from the end. function insertTimestampBeforeFinalBrace(content, timestampBlock) { // Normalize line endings const c = content.replace(/\r\n/g, '\n'); // Remove trailing whitespace/newlines const trimmed = c.replace(/\s+$/, ''); // The last character should be '}' if (!trimmed.endsWith('}')) { throw new Error('File does not end with }'); } // Find content before the final '}' // Find last '}' position const lastCloseIdx = trimmed.lastIndexOf('}'); const beforeClosing = trimmed.slice(0, lastCloseIdx); // Remove trailing whitespace/newlines from beforeClosing to get to the last property const beforeTrimmed = beforeClosing.replace(/\s+$/, ''); // Determine if we need a comma: check if the last char of beforeTrimmed ends with a comma // But also: if beforeTrimmed ends with } (closing of nested object at top-level), // we may still need a comma. E.g.: "xxx": { ... } } -> beforeTrimmed ends with "..."xxx": { ... } // In that case beforeTrimmed ends with } (of a nested obj), we need to add comma before adding our timestamp const needsComma = !beforeTrimmed.endsWith(','); const result = beforeTrimmed + (needsComma ? ',' : '') + '\n' + timestampBlock + '\n}'; return result; } ['zh', 'en', 'ja', 'ko', 'ru', 'zh-TW'].forEach(lang => { const filePath = path.join(localesDir, lang + '.json'); const content = fs.readFileSync(filePath, 'utf8'); // Check if already has timestamp (by string pattern) if (content.indexOf('"timestamp"') !== -1) { console.log(lang + '.json already has "timestamp" entry, skipping'); return; } const timestampBlock = formatTimestampBlock(translations[lang].timestamp); try { const newContent = insertTimestampBeforeFinalBrace(content, timestampBlock); fs.writeFileSync(filePath, newContent + '\n', 'utf8'); console.log(lang + '.json: updated OK'); } catch (e) { console.error(lang + '.json: FAILED - ' + e.message); } }); console.log('Done.');