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:
kerms 2026-02-28 11:50:00 +01:00
parent d32674617d
commit 4959ff74c1
Signed by: kerms
GPG Key ID: 5432C10DDCF8DAD5
9 changed files with 57 additions and 57 deletions

View File

@ -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);

View File

@ -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;

View File

@ -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.`
);
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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);

View File

@ -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}`);
}
}

View File

@ -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}" 不是有效的字节值 (0255): ${val}`);
throw new Error(`"${field}" is not a valid byte value (0255): ${val}`);
}
function assertU32(val: number, field: string): void {
if (!Number.isInteger(val) || val < 0 || val > U32_MAX)
throw new Error(`"${field}" 不是有效的 32 位无符号整数 (00xFFFFFFFF): ${val}`);
throw new Error(`"${field}" is not a valid uint32 (00xFFFFFFFF): ${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;

View File

@ -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,
});