A product slug, e.g. sodium-metasilicate-anhydrous.
The humanized product name.
humanizeSlug('isopropyl-alcohol-70-v-v-aqueous-solution');
// "isopropyl alcohol 70% (v/v) aqueous solution"
export function humanizeSlug(slug: string): string {
const parts = slug.split('-').filter(Boolean);
const out: string[] = [];
for (let i = 0; i < parts.length; i++) {
const cur = parts[i];
const next = parts[i + 1];
const prev = out[out.length - 1];
// "1-2-dichloroethane" -> "1,2-dichloroethane": leading digits are locants.
if (
i === 0 &&
DIGITS.test(cur) &&
next &&
DIGITS.test(next) &&
parts[i + 2] &&
!DIGITS.test(parts[i + 2])
) {
out.push(`${cur},${next}-${parts[i + 2]}`);
i += 2;
continue;
}
// "p-xylene", "tert-butyl": stereo/position prefixes keep their hyphen.
if (i === 0 && next && CHEM_PREFIX.test(cur)) {
out.push(`${cur}-${next}`);
i++;
continue;
}
// "v-v" -> "(v/v)"; a bare number in front of it is a percentage.
if (next && RATIO.test(cur) && RATIO.test(next)) {
if (prev && /\d$/.test(prev)) out[out.length - 1] = `${prev}%`;
out.push(`(${cur}/${next})`);
i++;
continue;
}
// "1g-l" / "10-g-l" -> "1g/L" / "10 g/L"
if (next === 'l' && VOLUME_UNIT.test(cur.replace(/^\d+/, ''))) {
out.push(`${cur}/L`);
i++;
continue;
}
if (next && DIGITS.test(cur)) {
// Leading zero is always a decimal: "0-0855" -> "0.0855"
if (cur.startsWith('0') && DIGITS.test(next)) {
out.push(`${cur}.${next}`);
i++;
continue;
}
// Three digits following is a thousands group: "1-000ppm" -> "1,000ppm"
if (LEADS_3_DIGITS.test(next)) {
out.push(`${cur},${next}`);
i++;
continue;
}
// Ascending multi-digit pair is a range: "20-30-mesh" -> "20-30 mesh"
if (DIGITS.test(next) && cur.length >= 2 && next.length >= 2 && Number(next) > Number(cur)) {
out.push(`${cur}-${next}`);
i++;
continue;
}
// Otherwise a decimal: "6-0-normal" -> "6.0 normal"
if (DIGITS.test(next)) {
out.push(`${cur}.${next}`);
i++;
continue;
}
}
out.push(cur);
}
for (let i = 0; i < out.length - 1; i++) {
// "10 aqueous solution" -> "10% aqueous solution"; also catches decimals.
if (/^\d+([.,]\d+)?$/.test(out[i]) && CONCENTRATION_CONTEXT.has(out[i + 1])) {
out[i] = `${out[i]}%`;
}
// Conductivity standards: "1,500 s solution" -> "1,500 µS solution"
if (out[i + 1] === 's' && /^\d+,\d{3}$/.test(out[i])) out[i + 1] = 'µS';
}
return out.join(' ');
}
Turns a product slug back into something close to the store's product title.
Slugs flatten punctuation to hyphens, so this reverses the common cases:
v-v/w-w/w-v→(v/v), and marks the number before it as a percent10-aqueous-solution→10% aqueous solution1-2-dichloroethane→1,2-dichloroethane1-000ppm→1,000ppm20-30-mesh→20-30 mesh0-100-normal→0.100 normal1g-l/10-g-l→1g/L/10 g/Lp-xylene→ keeps the stereo/position prefix hyphenMeasured at ~74% exact match (case- and comma-insensitive) against real product titles; the remainder is unrecoverable from a slug (commas, editorial parentheticals, degree signs,
#,=,+). The real title is recovered from the product page later, so this only needs to be good enough to rank matches.