refactor: translate error messages from Chinese to English
Standardize all user-facing error/validation messages across partition-table, nvs, app-image, and ota-data parsers to English.
This commit is contained in:
parent
d32674617d
commit
4959ff74c1
|
|
@ -15,13 +15,13 @@ import {
|
|||
*/
|
||||
export function parseAppImage(data: Uint8Array): AppImageInfo {
|
||||
if (data.length < IMAGE_HEADER_SIZE + EXTENDED_HEADER_SIZE) {
|
||||
throw new Error(`数据太短: ${data.length} 字节 (最少需要 ${IMAGE_HEADER_SIZE + EXTENDED_HEADER_SIZE} 字节)`);
|
||||
throw new Error(`Image too short: ${data.length}B`);
|
||||
}
|
||||
|
||||
// ── Image Header (8 bytes: magic + segments + spi_mode + spi_speed_size + entry_addr) ──
|
||||
const magic = readU8(data, 0);
|
||||
if (magic !== IMAGE_MAGIC) {
|
||||
throw new Error(`无效的魔数: 0x${magic.toString(16)} (应为 0xE9)`);
|
||||
throw new Error(`Invalid image magic: 0x${magic.toString(16)}`);
|
||||
}
|
||||
|
||||
const segmentCount = readU8(data, 1);
|
||||
|
|
|
|||
|
|
@ -66,10 +66,10 @@ interface ParsedPage {
|
|||
*/
|
||||
export function parseBinary(data: Uint8Array): NvsPartition {
|
||||
if (data.length % PAGE_SIZE !== 0) {
|
||||
throw new Error(`二进制数据大小 (${data.length}) 不是页大小 (${PAGE_SIZE}) 的倍数`);
|
||||
throw new Error(`Invalid NVS size: ${data.length}B`);
|
||||
}
|
||||
if (data.length === 0) {
|
||||
throw new Error('二进制数据为空');
|
||||
throw new Error('NVS data is empty');
|
||||
}
|
||||
|
||||
const pageCount = data.length / PAGE_SIZE;
|
||||
|
|
|
|||
|
|
@ -62,10 +62,10 @@ interface PlannedEntry {
|
|||
*/
|
||||
export function serializeBinary(partition: NvsPartition, targetSize: number): Uint8Array {
|
||||
if (targetSize % PAGE_SIZE !== 0) {
|
||||
throw new Error(`目标大小 (${targetSize}) 不是页大小 (${PAGE_SIZE}) 的倍数`);
|
||||
throw new Error(`Target size (${targetSize}) is not a multiple of page size (${PAGE_SIZE})`);
|
||||
}
|
||||
if (targetSize < MIN_PARTITION_SIZE) {
|
||||
throw new Error(`目标大小 (${targetSize}) 小于最小分区大小 (${MIN_PARTITION_SIZE})`);
|
||||
throw new Error(`Target size (${targetSize}) is less than minimum partition size (${MIN_PARTITION_SIZE})`);
|
||||
}
|
||||
|
||||
// Allocate buffer filled with 0xFF (erased flash state)
|
||||
|
|
@ -328,8 +328,8 @@ export function serializeBinary(partition: NvsPartition, targetSize: number): Ui
|
|||
|
||||
if (plannedIdx < planned.length) {
|
||||
throw new Error(
|
||||
`分区空间不足: 还有 ${planned.length - plannedIdx} 个条目无法写入。` +
|
||||
`请增大分区大小。`
|
||||
`Partition space exhausted: ${planned.length - plannedIdx} entries could not be written. ` +
|
||||
`Increase partition size.`
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,17 +40,17 @@ function parseIntValue(str: string): number {
|
|||
let val: number;
|
||||
if (str.startsWith('0x') || str.startsWith('0X')) {
|
||||
if (!/^-?0[xX][0-9a-fA-F]+$/.test(str)) {
|
||||
throw new Error(`无效的整数值: "${str}"`);
|
||||
throw new Error(`Invalid integer value: "${str}"`);
|
||||
}
|
||||
val = parseInt(str, 16);
|
||||
} else {
|
||||
if (!/^-?\d+$/.test(str)) {
|
||||
throw new Error(`无效的整数值: "${str}"`);
|
||||
throw new Error(`Invalid integer value: "${str}"`);
|
||||
}
|
||||
val = parseInt(str, 10);
|
||||
}
|
||||
if (Number.isNaN(val)) {
|
||||
throw new Error(`无效的整数值: "${str}"`);
|
||||
throw new Error(`Invalid integer value: "${str}"`);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
|
@ -62,14 +62,14 @@ function parseBigIntValue(str: string): bigint {
|
|||
// Handle negative hex explicitly.
|
||||
if (str.startsWith('-0x') || str.startsWith('-0X')) {
|
||||
if (!/^-0[xX][0-9a-fA-F]+$/.test(str)) {
|
||||
throw new Error(`无效的整数值: "${str}"`);
|
||||
throw new Error(`Invalid integer value: "${str}"`);
|
||||
}
|
||||
return -BigInt(str.slice(1));
|
||||
}
|
||||
try {
|
||||
return BigInt(str);
|
||||
} catch {
|
||||
throw new Error(`无效的整数值: "${str}"`);
|
||||
throw new Error(`Invalid integer value: "${str}"`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -169,20 +169,20 @@ export function parseCsv(text: string): NvsPartition {
|
|||
}
|
||||
|
||||
if (!currentNamespace) {
|
||||
throw new Error(`行 ${i + 1}: 数据条目 "${key}" 出现在任何命名空间之前`);
|
||||
throw new Error(`Line ${i + 1}: data entry "${key}" appears before any namespace`);
|
||||
}
|
||||
|
||||
if (type !== 'data' && type !== 'file') {
|
||||
throw new Error(`行 ${i + 1}: 未知类型 "${type}"`);
|
||||
throw new Error(`Line ${i + 1}: unknown type "${type}"`);
|
||||
}
|
||||
|
||||
if (!encoding) {
|
||||
throw new Error(`行 ${i + 1}: 键 "${key}" 缺少编码类型`);
|
||||
throw new Error(`Line ${i + 1}: key "${key}" missing encoding`);
|
||||
}
|
||||
|
||||
const nvsType = ENCODING_TO_TYPE[encoding as NvsEncoding];
|
||||
if (nvsType === undefined) {
|
||||
throw new Error(`行 ${i + 1}: 未知编码 "${encoding}"`);
|
||||
throw new Error(`Line ${i + 1}: unknown encoding "${encoding}"`);
|
||||
}
|
||||
|
||||
let parsedValue: number | bigint | string | Uint8Array;
|
||||
|
|
|
|||
|
|
@ -182,27 +182,27 @@ export function validatePartition(partition: NvsPartition): string[] {
|
|||
const errors: string[] = [];
|
||||
|
||||
if (partition.namespaces.length > MAX_NAMESPACES) {
|
||||
errors.push(`命名空间数量超过上限 ${MAX_NAMESPACES}`);
|
||||
errors.push(`Namespace count exceeds limit ${MAX_NAMESPACES}`);
|
||||
}
|
||||
|
||||
for (const ns of partition.namespaces) {
|
||||
if (ns.length === 0) {
|
||||
errors.push('命名空间名称不能为空');
|
||||
errors.push('Namespace name cannot be empty');
|
||||
}
|
||||
if (ns.length > MAX_KEY_LENGTH) {
|
||||
errors.push(`命名空间 "${ns}" 名称超过 ${MAX_KEY_LENGTH} 字符`);
|
||||
errors.push(`Namespace "${ns}" exceeds ${MAX_KEY_LENGTH} characters`);
|
||||
}
|
||||
}
|
||||
|
||||
for (const entry of partition.entries) {
|
||||
if (entry.key.length === 0) {
|
||||
errors.push(`在命名空间 "${entry.namespace}" 中存在空键名`);
|
||||
errors.push(`Empty key in namespace "${entry.namespace}"`);
|
||||
}
|
||||
if (entry.key.length > MAX_KEY_LENGTH) {
|
||||
errors.push(`键 "${entry.key}" 名称超过 ${MAX_KEY_LENGTH} 字符`);
|
||||
errors.push(`Key "${entry.key}" exceeds ${MAX_KEY_LENGTH} characters`);
|
||||
}
|
||||
if (!partition.namespaces.includes(entry.namespace)) {
|
||||
errors.push(`键 "${entry.key}" 的命名空间 "${entry.namespace}" 未注册`);
|
||||
errors.push(`Key "${entry.key}" references unregistered namespace "${entry.namespace}"`);
|
||||
}
|
||||
|
||||
// Validate value ranges for primitive types
|
||||
|
|
@ -210,21 +210,21 @@ export function validatePartition(partition: NvsPartition): string[] {
|
|||
if (typeof entry.value === 'number') {
|
||||
const v = entry.value;
|
||||
switch (entry.type) {
|
||||
case NvsType.U8: if (v < 0 || v > 0xFF) errors.push(`"${entry.key}" U8 值超出范围`); break;
|
||||
case NvsType.I8: if (v < -128 || v > 127) errors.push(`"${entry.key}" I8 值超出范围`); break;
|
||||
case NvsType.U16: if (v < 0 || v > 0xFFFF) errors.push(`"${entry.key}" U16 值超出范围`); break;
|
||||
case NvsType.I16: if (v < -32768 || v > 32767) errors.push(`"${entry.key}" I16 值超出范围`); break;
|
||||
case NvsType.U32: if (v < 0 || v > 0xFFFFFFFF) errors.push(`"${entry.key}" U32 值超出范围`); break;
|
||||
case NvsType.I32: if (v < -2147483648 || v > 2147483647) errors.push(`"${entry.key}" I32 值超出范围`); break;
|
||||
case NvsType.U8: if (v < 0 || v > 0xFF) errors.push(`"${entry.key}" U8 value out of range`); break;
|
||||
case NvsType.I8: if (v < -128 || v > 127) errors.push(`"${entry.key}" I8 value out of range`); break;
|
||||
case NvsType.U16: if (v < 0 || v > 0xFFFF) errors.push(`"${entry.key}" U16 value out of range`); break;
|
||||
case NvsType.I16: if (v < -32768 || v > 32767) errors.push(`"${entry.key}" I16 value out of range`); break;
|
||||
case NvsType.U32: if (v < 0 || v > 0xFFFFFFFF) errors.push(`"${entry.key}" U32 value out of range`); break;
|
||||
case NvsType.I32: if (v < -2147483648 || v > 2147483647) errors.push(`"${entry.key}" I32 value out of range`); break;
|
||||
}
|
||||
} else if (typeof entry.value === 'bigint') {
|
||||
const v = entry.value;
|
||||
switch (entry.type) {
|
||||
case NvsType.U64:
|
||||
if (v < 0n || v > 0xFFFFFFFFFFFFFFFFn) errors.push(`"${entry.key}" U64 值超出范围`);
|
||||
if (v < 0n || v > 0xFFFFFFFFFFFFFFFFn) errors.push(`"${entry.key}" U64 value out of range`);
|
||||
break;
|
||||
case NvsType.I64:
|
||||
if (v < -9223372036854775808n || v > 9223372036854775807n) errors.push(`"${entry.key}" I64 值超出范围`);
|
||||
if (v < -9223372036854775808n || v > 9223372036854775807n) errors.push(`"${entry.key}" I64 value out of range`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -234,7 +234,7 @@ export function validatePartition(partition: NvsPartition): string[] {
|
|||
if (entry.type === NvsType.SZ && typeof entry.value === 'string') {
|
||||
const byteLen = new TextEncoder().encode(entry.value).length;
|
||||
if (byteLen >= MAX_STRING_LENGTH) {
|
||||
errors.push(`"${entry.key}" 字符串长度 ${byteLen} 字节超过上限 ${MAX_STRING_LENGTH - 1}`);
|
||||
errors.push(`"${entry.key}" string length ${byteLen} bytes exceeds limit ${MAX_STRING_LENGTH - 1}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -244,11 +244,11 @@ export function validatePartition(partition: NvsPartition): string[] {
|
|||
// NvsType.BLOB_DATA uses the V2 chunked format and is capped at MAX_BLOB_SIZE_V2.
|
||||
if (entry.type === NvsType.BLOB && entry.value instanceof Uint8Array) {
|
||||
if (entry.value.length > MAX_BLOB_SIZE_V1) {
|
||||
errors.push(`"${entry.key}" BLOB ${entry.value.length} 字节超过上限 ${MAX_BLOB_SIZE_V1}`);
|
||||
errors.push(`"${entry.key}" BLOB ${entry.value.length} bytes exceeds limit ${MAX_BLOB_SIZE_V1}`);
|
||||
}
|
||||
} else if (entry.type === NvsType.BLOB_DATA && entry.value instanceof Uint8Array) {
|
||||
if (entry.value.length > MAX_BLOB_SIZE_V2) {
|
||||
errors.push(`"${entry.key}" BLOB ${entry.value.length} 字节超过 V2 上限 ${MAX_BLOB_SIZE_V2}`);
|
||||
errors.push(`"${entry.key}" BLOB ${entry.value.length} bytes exceeds V2 limit ${MAX_BLOB_SIZE_V2}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -258,7 +258,7 @@ export function validatePartition(partition: NvsPartition): string[] {
|
|||
for (const entry of partition.entries) {
|
||||
const k = `${entry.namespace}::${entry.key}`;
|
||||
if (seen.has(k)) {
|
||||
errors.push(`重复键: ${entry.namespace}/${entry.key}`);
|
||||
errors.push(`Duplicate key: ${entry.namespace}/${entry.key}`);
|
||||
}
|
||||
seen.add(k);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ function isEntryValid(entry: OtaSelectEntry): boolean {
|
|||
*/
|
||||
export function parseOtaData(data: Uint8Array, numOtaPartitions = DEFAULT_NUM_OTA_PARTITIONS): OtaData {
|
||||
if (data.length < OTA_DATA_MIN_SIZE) {
|
||||
throw new Error(`OTA data too short: ${data.length} bytes, need at least ${OTA_DATA_MIN_SIZE}`);
|
||||
throw new Error(`OTA data too short: ${data.length}B`);
|
||||
}
|
||||
|
||||
const entry0 = parseEntry(data, 0);
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ function parseFlags(str: string): number {
|
|||
for (const part of normalized.split(/[\s|,]+/).filter(Boolean)) {
|
||||
if (part === 'encrypted') result |= PartitionFlags.ENCRYPTED;
|
||||
else if (part === 'readonly') result |= PartitionFlags.READONLY;
|
||||
else throw new Error(`未知标志: "${part}"`);
|
||||
else throw new Error(`Unknown flag: "${part}"`);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
@ -26,9 +26,9 @@ function parseSize(str: string): number {
|
|||
str = str.trim();
|
||||
if (!str) return 0;
|
||||
if (str.startsWith('0x') || str.startsWith('0X')) {
|
||||
if (!/^0x[0-9a-f]+$/i.test(str)) throw new Error(`无效的大小/偏移值: "${str}"`);
|
||||
if (!/^0x[0-9a-f]+$/i.test(str)) throw new Error(`Invalid size/offset value: "${str}"`);
|
||||
const v = parseInt(str, 16);
|
||||
if (isNaN(v) || v < 0 || v > U32_MAX) throw new Error(`无效的大小/偏移值: "${str}"`);
|
||||
if (isNaN(v) || v < 0 || v > U32_MAX) throw new Error(`Invalid size/offset value: "${str}"`);
|
||||
return v;
|
||||
}
|
||||
|
||||
|
|
@ -40,11 +40,11 @@ function parseSize(str: string): number {
|
|||
if (unit === 'K') result = Math.floor(num * 1024);
|
||||
else if (unit === 'M') result = Math.floor(num * 1024 * 1024);
|
||||
else result = Math.floor(num);
|
||||
if (result > U32_MAX) throw new Error(`无效的大小/偏移值: "${str}" (超出 32 位范围)`);
|
||||
if (result > U32_MAX) throw new Error(`Invalid size/offset value: "${str}" (exceeds 32-bit range)`);
|
||||
return result;
|
||||
}
|
||||
|
||||
throw new Error(`无效的大小/偏移值: "${str}"`);
|
||||
throw new Error(`Invalid size/offset value: "${str}"`);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -86,7 +86,7 @@ function splitCsvLine(line: string): string[] {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (inQuotes) throw new Error('引号未闭合');
|
||||
if (inQuotes) throw new Error('Unclosed quote');
|
||||
fields.push(current.trim());
|
||||
return fields;
|
||||
}
|
||||
|
|
@ -111,7 +111,7 @@ export function parseCsv(text: string, onWarning?: (line: number, message: strin
|
|||
try {
|
||||
const fields = splitCsvLine(line);
|
||||
if (fields.length < 5) {
|
||||
onWarning?.(lineIdx + 1, `字段数量不足 (需要 5,实际 ${fields.length}): "${line}"`);
|
||||
onWarning?.(lineIdx + 1, `Not enough fields (need 5, got ${fields.length}): "${line}"`);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -127,7 +127,7 @@ export function parseCsv(text: string, onWarning?: (line: number, message: strin
|
|||
|
||||
const type = NAME_TO_TYPE[typeName];
|
||||
if (type === undefined) {
|
||||
onWarning?.(lineIdx + 1, `未知分区类型: "${typeName}"`);
|
||||
onWarning?.(lineIdx + 1, `Unknown partition type: "${typeName}"`);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -137,7 +137,7 @@ export function parseCsv(text: string, onWarning?: (line: number, message: strin
|
|||
const flags = parseFlags(flagsStr);
|
||||
entries.push({ name, type, subtype, offset, size, flags });
|
||||
} catch (e) {
|
||||
onWarning?.(lineIdx + 1, `解析失败: ${(e as Error).message}`);
|
||||
onWarning?.(lineIdx + 1, `Parse failed: ${(e as Error).message}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,12 +7,12 @@ const U32_MAX = 0xFFFF_FFFF;
|
|||
|
||||
function assertU8(val: number, field: string): void {
|
||||
if (!Number.isInteger(val) || val < 0 || val > 0xFF)
|
||||
throw new Error(`"${field}" 不是有效的字节值 (0–255): ${val}`);
|
||||
throw new Error(`"${field}" is not a valid byte value (0–255): ${val}`);
|
||||
}
|
||||
|
||||
function assertU32(val: number, field: string): void {
|
||||
if (!Number.isInteger(val) || val < 0 || val > U32_MAX)
|
||||
throw new Error(`"${field}" 不是有效的 32 位无符号整数 (0–0xFFFFFFFF): ${val}`);
|
||||
throw new Error(`"${field}" is not a valid uint32 (0–0xFFFFFFFF): ${val}`);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -26,14 +26,14 @@ export function serializeBinary(table: PartitionTable): Uint8Array {
|
|||
let offset = 0;
|
||||
for (const entry of table.entries) {
|
||||
if (offset + ENTRY_SIZE > TABLE_MAX_SIZE - ENTRY_SIZE) {
|
||||
throw new Error(`分区表条目过多,超过最大容量 (最多 ${Math.floor((TABLE_MAX_SIZE - ENTRY_SIZE) / ENTRY_SIZE)} 条)`);
|
||||
throw new Error(`Too many partition table entries (max ${Math.floor((TABLE_MAX_SIZE - ENTRY_SIZE) / ENTRY_SIZE)})`);
|
||||
}
|
||||
|
||||
assertU8(entry.type, '类型');
|
||||
assertU8(entry.subtype, '子类型');
|
||||
assertU32(entry.offset, '偏移量');
|
||||
assertU32(entry.size, '大小');
|
||||
assertU32(entry.flags, '标志');
|
||||
assertU8(entry.type, 'type');
|
||||
assertU8(entry.subtype, 'subtype');
|
||||
assertU32(entry.offset, 'offset');
|
||||
assertU32(entry.size, 'size');
|
||||
assertU32(entry.flags, 'flags');
|
||||
|
||||
writeU16(buf, offset, ENTRY_MAGIC);
|
||||
buf[offset + 2] = entry.type;
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ export function validateTable(table: PartitionTable): PartitionValidationError[]
|
|||
if (names.has(entry.name)) {
|
||||
errors.push({
|
||||
type: 'duplicate_name',
|
||||
message: `分区名称重复: "${entry.name}"`,
|
||||
message: `Duplicate partition name: "${entry.name}"`,
|
||||
entryA: names.get(entry.name),
|
||||
entryB: entry,
|
||||
});
|
||||
|
|
@ -33,14 +33,14 @@ export function validateTable(table: PartitionTable): PartitionValidationError[]
|
|||
if (entry.offset !== 0 && entry.offset % SECTOR_SIZE !== 0) {
|
||||
errors.push({
|
||||
type: 'alignment',
|
||||
message: `"${entry.name}" 偏移 0x${entry.offset.toString(16)} 未对齐到 4KB 边界`,
|
||||
message: `"${entry.name}" offset 0x${entry.offset.toString(16)} not aligned to 4KB boundary`,
|
||||
entryA: entry,
|
||||
});
|
||||
}
|
||||
if (entry.size !== 0 && entry.size % SECTOR_SIZE !== 0) {
|
||||
errors.push({
|
||||
type: 'alignment',
|
||||
message: `"${entry.name}" 大小 0x${entry.size.toString(16)} 未对齐到 4KB 边界`,
|
||||
message: `"${entry.name}" size 0x${entry.size.toString(16)} not aligned to 4KB boundary`,
|
||||
entryA: entry,
|
||||
});
|
||||
}
|
||||
|
|
@ -57,7 +57,7 @@ export function validateTable(table: PartitionTable): PartitionValidationError[]
|
|||
if (a.offset < bEnd && b.offset < aEnd) {
|
||||
errors.push({
|
||||
type: 'overlap',
|
||||
message: `"${a.name}" [0x${a.offset.toString(16)}..0x${aEnd.toString(16)}] 与 "${b.name}" [0x${b.offset.toString(16)}..0x${bEnd.toString(16)}] 重叠`,
|
||||
message: `"${a.name}" [0x${a.offset.toString(16)}..0x${aEnd.toString(16)}] overlaps "${b.name}" [0x${b.offset.toString(16)}..0x${bEnd.toString(16)}]`,
|
||||
entryA: a,
|
||||
entryB: b,
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue