365 lines
13 KiB
JavaScript
365 lines
13 KiB
JavaScript
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.');
|