65 lines
2.4 KiB
TypeScript
65 lines
2.4 KiB
TypeScript
/**
|
||
* Copyright since 2025 Mifos Initiative
|
||
*
|
||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||
*/
|
||
|
||
/**
|
||
* Casing for client names: the name is stored exactly as the officer typed it, except that a word
|
||
* starting with a lower-case letter has that first letter raised. Nothing else is touched.
|
||
*
|
||
* 'keshav Singh sisodiya' → 'Keshav Singh Sisodiya'
|
||
* 'lincpay lTD' → 'Lincpay LTD'
|
||
* 'Technoludium Solutions Private Limited' → unchanged
|
||
* 'TECHNOLUDIUM SOLUTIONS' → unchanged
|
||
*
|
||
* Deliberately NOT full title-casing. Lower-casing the rest of each word would rewrite an entity
|
||
* name the officer entered as it appears on the certificate — 'TECHNOLUDIUM' becoming
|
||
* 'Technoludium', 'Ltd' becoming a forced 'LTD'. That name is matched character-for-character
|
||
* against the PAN holder's registered name during KYC verification, so the typed spelling is the
|
||
* thing to preserve; only an unintended lower-case first letter is corrected.
|
||
*/
|
||
|
||
/** Characters after which a new word starts: 'jean-marie' → 'Jean-Marie'. */
|
||
const SEGMENT_STARTERS = new Set([
|
||
'-',
|
||
"'",
|
||
'’'
|
||
]);
|
||
|
||
/**
|
||
* Raises the first letter of each word in a single space-delimited token, leaving every other
|
||
* character as typed.
|
||
*/
|
||
function capitalizeWord(word: string): string {
|
||
let result = '';
|
||
for (let i = 0; i < word.length; i++) {
|
||
const previous = i === 0 ? '' : word[i - 1];
|
||
let startsWord = i === 0 || SEGMENT_STARTERS.has(previous);
|
||
if (previous === "'" || previous === '’') {
|
||
// 'o'brien' / 'd'souza' capitalise after the apostrophe, but a possessive
|
||
// ('lincpay's') must not — only a single leading letter marks the name prefix.
|
||
startsWord = i === 2;
|
||
}
|
||
result += startsWord ? word[i].toUpperCase() : word[i];
|
||
}
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* Applies the casing rule above to a person or entity name.
|
||
* Trims and collapses runs of whitespace; non-string input yields ''.
|
||
*/
|
||
export function toProperNameCase(value: string | null | undefined): string {
|
||
if (typeof value !== 'string') {
|
||
return '';
|
||
}
|
||
const collapsed = value.trim().replace(/\s+/g, ' ');
|
||
if (!collapsed) {
|
||
return '';
|
||
}
|
||
return collapsed.split(' ').map(capitalizeWord).join(' ');
|
||
}
|