spellKoreanNumber function

String spellKoreanNumber(
  1. String number, {
  2. bool sino = true,
})

Spells one comma-grouped decimal integer using pinned g2pkc number rules.

Upstream's Unicode \d matcher accepts every Unicode decimal digit, while its digit-name tables intentionally contain ASCII keys only. That observable mismatch is preserved here.

Pinned g2pkc defines positions 0 through 15. Input containing more than 16 comma-free digits throws InvalidConfigurationException, preserving its no-output failure boundary with an actionable typed package error. Empty input, non-decimal characters, and misplaced non-comma separators throw the same exception.

Implementation

String spellKoreanNumber(String number, {bool sino = true}) {
  final digitsOnly = number.replaceAll(',', '');
  final digits = <String>[
    for (final scalar in digitsOnly.runes) String.fromCharCode(scalar),
  ];
  if (digits.isEmpty ||
      digitsOnly.runes.any((scalar) => !isPython312DecimalScalar(scalar))) {
    throw const InvalidConfigurationException(
      'A Korean number must contain at least one Unicode decimal digit and '
      'otherwise only commas.',
    );
  }
  if (digits.length > 16) {
    throw InvalidConfigurationException(
      'Pinned g2pkc leaves Korean numeral positions above 15 undefined; '
      'received ${digits.length} comma-free digits.',
    );
  }
  if (digitsOnly == '0') {
    return '영';
  }
  if (!sino && digitsOnly == '20') {
    return '스무';
  }

  final output = <String>[];
  for (var index = 0; index < digits.length; index++) {
    final digit = digits[index];
    final position = digits.length - index - 1;
    var name = '';
    if (sino || digits.length >= 3) {
      if (position == 0) {
        name = _sinoDigitNames[digit] ?? '';
      } else if (position == 1) {
        name = '${_sinoDigitNames[digit] ?? ''}십'.replaceFirst('일십', '십');
      }
    } else if (position == 0) {
      name = _nativeModifiers[digit] ?? '';
    } else if (position == 1) {
      name = _nativeTens[digit] ?? '';
    }

    if (digit == '0') {
      if (position % 4 == 0) {
        final start = output.length < 3 ? 0 : output.length - 3;
        if (output.sublist(start).join().isEmpty) {
          output.add('');
          continue;
        }
      } else {
        output.add('');
        continue;
      }
    }

    if (position == 2) {
      name = '${_sinoDigitNames[digit] ?? ''}백'.replaceFirst('일백', '백');
    } else if (position == 3) {
      name = '${_sinoDigitNames[digit] ?? ''}천'.replaceFirst('일천', '천');
    } else if (position == 4) {
      name = '${_sinoDigitNames[digit] ?? ''}만'.replaceFirst('일만', '만');
    } else if (position == 5) {
      name = '${_sinoDigitNames[digit] ?? ''}십'.replaceFirst('일십', '십');
    } else if (position == 6) {
      name = '${_sinoDigitNames[digit] ?? ''}백'.replaceFirst('일백', '백');
    } else if (position == 7) {
      name = '${_sinoDigitNames[digit] ?? ''}천'.replaceFirst('일천', '천');
    } else if (position == 8) {
      name = '${_sinoDigitNames[digit] ?? ''}억';
    } else if (position == 9) {
      name = '${_sinoDigitNames[digit] ?? ''}십';
    } else if (position == 10) {
      name = '${_sinoDigitNames[digit] ?? ''}백';
    } else if (position == 11) {
      name = '${_sinoDigitNames[digit] ?? ''}천';
    } else if (position == 12) {
      name = '${_sinoDigitNames[digit] ?? ''}조';
    } else if (position == 13) {
      name = '${_sinoDigitNames[digit] ?? ''}십';
    } else if (position == 14) {
      name = '${_sinoDigitNames[digit] ?? ''}백';
    } else if (position == 15) {
      name = '${_sinoDigitNames[digit] ?? ''}천';
    }
    output.add(name);
  }
  return output.join();
}