Skip to content

Commit fa3b9e0

Browse files
brianfunkclaude
andcommitted
fix(pt): add missing portuguese export and fix "e" connector
- Add portuguese to named exports in index.js (Codex review) - Fix Portuguese join logic to correctly use "e" between higher scale groups (milhão/bilhão) and thousands (mil) - Add comprehensive Portuguese test suite with 8 test cases - Add toWords test for Portuguese language Fixes the issue where "um milhão mil" was output instead of the correct "um milhão e mil" for 1,001,000. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent c5db19e commit fa3b9e0

3 files changed

Lines changed: 70 additions & 5 deletions

File tree

index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
import { english, spanish, french, german, danish, chinese, hindi, russian, portuguese, LANGUAGES } from './languages/index.js';
2323

2424
// Re-export language functions
25-
export { spanish, french, german, danish, chinese, hindi, russian };
25+
export { spanish, french, german, danish, chinese, hindi, russian, portuguese };
2626

2727
// ============================================================================
2828
// CONSTANTS

languages/pt.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,10 +117,17 @@ const portuguese = (n, opt) => {
117117
let result = '';
118118
for (let i = 0; i < parts.length; i++) {
119119
if (i > 0) {
120-
// Use "e" (and) for connecting in Portuguese
120+
const prevPart = parts[i - 1];
121121
const currPart = parts[i];
122-
// Add "e" when the current part is less than 100 or ends with 00
123-
if (currPart && !currPart.includes('mil') && !currPart.includes('ilhão') && !currPart.includes('ilhões')) {
122+
123+
// Use "e" (and) for connecting in Portuguese when:
124+
// 1. Current part has no scale word (final small numbers)
125+
// 2. Previous part has a higher scale (milhão+) and current part has "mil"
126+
const prevHasHigherScale = prevPart && (prevPart.includes('ilhão') || prevPart.includes('ilhões'));
127+
const currHasMil = currPart && currPart.includes('mil');
128+
const currHasNoScale = currPart && !currHasMil && !currPart.includes('ilhão') && !currPart.includes('ilhões');
129+
130+
if (currHasNoScale || (prevHasHigherScale && currHasMil)) {
124131
result += ' e ';
125132
} else {
126133
result += ' ';

test/index.test.js

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, it, expect } from 'vitest';
2-
import numberstring, { comma, group, ordinal, decimal, currency, roman, parse, negative, fraction, year, telephone, percent, spanish, french, german, danish, chinese, hindi, russian, toWords } from '../index.js';
2+
import numberstring, { comma, group, ordinal, decimal, currency, roman, parse, negative, fraction, year, telephone, percent, spanish, french, german, danish, chinese, hindi, russian, portuguese, toWords } from '../index.js';
33

44
describe('numberstring', () => {
55
describe('basic conversions', () => {
@@ -973,6 +973,64 @@ describe('toWords', () => {
973973
expect(toWords(42, { lang: 'russian' })).toBe('сорок два');
974974
expect(toWords(42, { lang: 'русский' })).toBe('сорок два');
975975
});
976+
977+
it('converts to Portuguese', () => {
978+
expect(toWords(42, { lang: 'pt' })).toBe('quarenta e dois');
979+
expect(toWords(42, { lang: 'portuguese' })).toBe('quarenta e dois');
980+
expect(toWords(42, { lang: 'português' })).toBe('quarenta e dois');
981+
});
982+
});
983+
984+
describe('portuguese', () => {
985+
it('converts basic numbers', () => {
986+
expect(portuguese(0)).toBe('zero');
987+
expect(portuguese(1)).toBe('um');
988+
expect(portuguese(10)).toBe('dez');
989+
expect(portuguese(15)).toBe('quinze');
990+
expect(portuguese(21)).toBe('vinte e um');
991+
expect(portuguese(42)).toBe('quarenta e dois');
992+
});
993+
994+
it('converts hundreds', () => {
995+
expect(portuguese(100)).toBe('cem');
996+
expect(portuguese(101)).toBe('cento e um');
997+
expect(portuguese(200)).toBe('duzentos');
998+
expect(portuguese(500)).toBe('quinhentos');
999+
expect(portuguese(123)).toBe('cento e vinte e três');
1000+
});
1001+
1002+
it('converts thousands', () => {
1003+
expect(portuguese(1000)).toBe('mil');
1004+
expect(portuguese(2000)).toBe('dois mil');
1005+
expect(portuguese(1001)).toBe('mil e um');
1006+
expect(portuguese(2500)).toBe('dois mil e quinhentos');
1007+
expect(portuguese(10000)).toBe('dez mil');
1008+
expect(portuguese(100000)).toBe('cem mil');
1009+
});
1010+
1011+
it('converts millions', () => {
1012+
expect(portuguese(1000000)).toBe('um milhão');
1013+
expect(portuguese(2000000)).toBe('dois milhões');
1014+
expect(portuguese(1500000)).toBe('um milhão e quinhentos mil');
1015+
});
1016+
1017+
it('uses "e" connector between higher scales and thousands', () => {
1018+
// This is the specific bug fix - "um milhão e mil" not "um milhão mil"
1019+
expect(portuguese(1001000)).toBe('um milhão e mil');
1020+
expect(portuguese(2001000)).toBe('dois milhões e mil');
1021+
expect(portuguese(1002000)).toBe('um milhão e dois mil');
1022+
});
1023+
1024+
it('applies capitalization', () => {
1025+
expect(portuguese(42, { cap: 'title' })).toBe('Quarenta E Dois');
1026+
expect(portuguese(42, { cap: 'upper' })).toBe('QUARENTA E DOIS');
1027+
});
1028+
1029+
it('returns false for invalid input', () => {
1030+
expect(portuguese(-1)).toBe(false);
1031+
expect(portuguese(NaN)).toBe(false);
1032+
expect(portuguese('abc')).toBe(false);
1033+
});
9761034
});
9771035

9781036
describe('comma', () => {

0 commit comments

Comments
 (0)