normalizeNameLikeLineTitleCase function
Applies title-case to all words in name-like lines.
This pass is intentionally narrow: only alphabetic lines with 2-4 tokens where at least one token already looks title-cased and at most one token is fully lowercase. This avoids changing normal sentence lines.
Implementation
String normalizeNameLikeLineTitleCase(String line) {
if (line.isEmpty || RegExp(r'[^A-Za-z\s]').hasMatch(line)) {
return line;
}
final List<String> tokens = line
.split(RegExp(r'\s+'))
.where((String token) => token.isNotEmpty)
.toList();
if (tokens.length < _nameLikeLineMinTokens ||
tokens.length > _nameLikeLineMaxTokens) {
return line;
}
int titleCaseTokens = 0;
int lowercaseTokens = 0;
int mixedCaseTokens = 0;
for (final String token in tokens) {
if (!isAlphaWord(token)) {
return line;
}
if (isTitleCaseWord(token)) {
titleCaseTokens++;
continue;
}
if (token == token.toLowerCase()) {
lowercaseTokens++;
continue;
}
if (isMixedCase(token)) {
mixedCaseTokens++;
continue;
}
return line;
}
if (titleCaseTokens == 0 || lowercaseTokens > 1 || mixedCaseTokens > 1) {
return line;
}
final List<String> normalized = <String>[];
for (int i = 0; i < tokens.length; i++) {
final String token = tokens[i];
if (i > 0 &&
token == token.toLowerCase() &&
token.length <= _titleCasePreserveLowerTokenMaxLength) {
normalized.add(token);
continue;
}
if (isMixedCase(token) && _countUppercaseAfterStart(token) > 1) {
normalized.add(token);
continue;
}
final String repaired = _normalizeNameLikeToken(token);
if (isMixedCase(token) && repaired == token) {
normalized.add(token);
continue;
}
normalized.add(toTitleCaseWord(repaired));
}
return normalized.join(' ');
}