Skip to content

Add Portuguese normalization for gladia-3 WER scoring.#30

Open
egenthon-cmd wants to merge 1 commit into
mainfrom
feat/portuguese-normalization
Open

Add Portuguese normalization for gladia-3 WER scoring.#30
egenthon-cmd wants to merge 1 commit into
mainfrom
feat/portuguese-normalization

Conversation

@egenthon-cmd

@egenthon-cmd egenthon-cmd commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Introduce a dedicated pt language pack with colloquial variants, clitic merging, written-number expansion, and Orbio call-center oriented rules (fillers, oral prepositions, gender/spelling variants, brand names).

What does this PR do?

Type of change

  • New language
  • Edit existing language (fix a replacement, tweak config, …)
  • New normalization step
  • Edit existing step (bug fix, behaviour change)
  • New preset version
  • Bug fix (other)
  • Refactor / docs / CI

Checklist

Only fill in the section(s) that match your change — delete the rest.


New language

  • Created normalization/languages/{lang}/ with operators.py, replacements.py, __init__.py
  • Word substitutions are in replacements.py (not hardcoded in operators.py)
  • LanguageConfig is filled in with the language's data (separators, currency words, digit words, …)
  • Subclassed LanguageOperators — only override methods where the logic changes, not just the data
  • Class is decorated with @register_language and imported in normalization/languages/__init__.py
  • Unit tests added in tests/unit/languages/
  • E2e CSV added in tests/e2e/files/{preset}/{lang}.csv (e.g. tests/e2e/files/gladia-3/fr.csv)

Edit existing language

  • New/changed word substitutions go in replacements.py, not inline in operators.py
  • If you changed a config field that can be None: the step reading it still handles None gracefully
  • Unit tests updated or added
  • E2e CSV updated if the expected output changed

New step

  • Unique name class attribute set (this is the key used in YAML presets)
  • Decorated with @register_step and imported in steps/text/__init__.py or steps/word/__init__.py
  • No hardcoded language values — read data from operators.config.* instead
  • If placeholder-based: protect + restore are both in steps/text/placeholders.py and pipeline/base.py's validate() is updated
  • Unit tests added in tests/unit/steps/
  • Step name added to the relevant preset YAML — or a new preset file created if existing presets are affected
  • If the docstring changed: ran uv run scripts/generate_step_docs.py

Edit existing step

  • Step name is unchanged — if the output changes, create a new step name + new preset instead
  • No language-specific logic or string literals added inside the step
  • Unit tests updated or added
  • If the docstring changed: ran uv run scripts/generate_step_docs.py

Preset change

  • Existing preset files are not modified — new behaviour goes in a new preset file
  • pipeline.validate() passes (runs automatically via loader.py)

How was this tested?

uv run pytest tests/

Summary by CodeRabbit

  • New Features

    • Added Portuguese language support to text normalization, including number conversion, clitic handling, and phrase-level cleanup.
    • Expanded the available Portuguese normalization rules for common spelling, contraction, email, and measurement variants.
  • Bug Fixes

    • Improved normalization of Portuguese written numbers into digits.
    • Fixed handling of hyphenated and spaced clitic forms so they normalize consistently.
    • Added coverage for Portuguese-specific wording and orthography variants to better match spoken input.

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@egenthon-cmd, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 42 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 41387311-d913-4503-b4b3-10a6ad307159

📥 Commits

Reviewing files that changed from the base of the PR and between d115d4d and b3361e5.

⛔ Files ignored due to path filters (1)
  • tests/e2e/files/gladia-3/pt.csv is excluded by !**/*.csv
📒 Files selected for processing (9)
  • normalization/languages/__init__.py
  • normalization/languages/portuguese/__init__.py
  • normalization/languages/portuguese/clitics.py
  • normalization/languages/portuguese/number_normalizer.py
  • normalization/languages/portuguese/operators.py
  • normalization/languages/portuguese/replacements.py
  • normalization/languages/portuguese/sentence_replacements.py
  • tests/unit/languages/portuguese_number_normalizer_test.py
  • tests/unit/languages/portuguese_operators_test.py
📝 Walkthrough

Walkthrough

Adds a new Portuguese language module to the normalization package, including clitic merging, written-number normalization via text2num, word/sentence replacement dictionaries, a PortugueseOperators class wiring these together, package exports, and corresponding unit tests.

Changes

Portuguese Language Normalization

Layer / File(s) Summary
Clitic merging utility
normalization/languages/portuguese/clitics.py
Adds regex-based detection and merging of hyphenated Portuguese clitic pronoun forms into single lowercased tokens.
Number normalizer
normalization/languages/portuguese/number_normalizer.py, tests/unit/languages/portuguese_number_normalizer_test.py
Implements PortugueseNumberNormalizer, restoring accents, rewriting mixed digit+word phrases, converting via alpha2digit, and fixing remaining unmapped words; tested with parametrized cases.
Word and sentence replacement dictionaries
normalization/languages/portuguese/replacements.py, normalization/languages/portuguese/sentence_replacements.py
Adds PORTUGUESE_REPLACEMENTS and PORTUGUESE_SENTENCE_REPLACEMENTS mappings for canonicalizing colloquial/ASR word and phrase variants.
PortugueseOperators and package wiring
normalization/languages/portuguese/operators.py, normalization/languages/portuguese/__init__.py, normalization/languages/__init__.py
Defines PORTUGUESE_CONFIG and PortugueseOperators, wiring clitic merging, number expansion, "um" numeric-context fixes, and word replacements; registers Portuguese module in package exports.
Operator behavior tests
tests/unit/languages/portuguese_operators_test.py
Adds parametrized tests covering clitic merging equivalence, filler removal, spelling variants, email/hyphen normalization, pt-pt/pt-br variants, brand expansion, and written-number normalization.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant PortugueseOperators
  participant PortugueseNumberNormalizer
  participant alpha2digit

  Caller->>PortugueseOperators: normalize(text)
  PortugueseOperators->>PortugueseOperators: expand_contractions (merge_hyphenated_clitics)
  PortugueseOperators->>PortugueseNumberNormalizer: expand_written_numbers(text)
  PortugueseNumberNormalizer->>alpha2digit: alpha2digit(text, "pt")
  alpha2digit-->>PortugueseNumberNormalizer: converted text
  PortugueseNumberNormalizer-->>PortugueseOperators: normalized numbers
  PortugueseOperators->>PortugueseOperators: fix_one_word_in_numeric_contexts
  PortugueseOperators->>PortugueseOperators: get_word_replacements (PORTUGUESE_REPLACEMENTS)
  PortugueseOperators-->>Caller: normalized text
Loading

Possibly related PRs

  • gladiaio/normalization#15: Both PRs modify normalization/languages/__init__.py's __all__ list to register new language modules.
  • gladiaio/normalization#19: Both PRs update normalization/languages/__init__.py to register a new language module in the same export list.

Suggested reviewers: karamouche, lrossillon-gladia

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.83% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: adding Portuguese normalization for gladia-3 WER scoring.
Description check ✅ Passed The description matches the new-language template and covers the main change, type, checklist, and testing section, though the PR summary section is only partially filled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/portuguese-normalization

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@egenthon-cmd egenthon-cmd force-pushed the feat/portuguese-normalization branch from d115d4d to 6cc7867 Compare July 9, 2026 18:20

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
normalization/languages/portuguese/clitics.py (1)

9-26: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Duplicate "lhes" in _CLITIC_SUFFIXES.

"lhes" appears at both Line 16 and Line 25. The regex alternation will match the first occurrence, so this is functionally harmless, but the duplicate should be removed for clarity.

♻️ Remove duplicate entry
     "lhe",
-    "lhes",
     "lhos",
     "los",
     "las",
     "no",
     "na",
     "lho",
     "lha",
-    "lhes",
 )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@normalization/languages/portuguese/clitics.py` around lines 9 - 26, The
_CLITIC_SUFFIXES tuple in clitics.py contains a duplicate "lhes" entry, so
remove the repeated value and keep only one occurrence. Update the
_CLITIC_SUFFIXES definition directly to preserve the intended suffix list and
keep the regex alternation clear and maintainable.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/unit/languages/portuguese_operators_test.py`:
- Around line 40-45: The assertions in portuguese_operators_test.py are
tautological because `pipeline.normalize(...)` is compared against itself, so
they never verify that meaningful affirmatives/idioms survive normalization.
Update the tests around the existing `pipeline.normalize` calls to compare each
input string against an explicit expected normalized result (or an expected
output fixture), using the same test cases for “claro estou”, “esta tudo bem”,
and “e pronto foi assim” so the `normalize` behavior is actually validated.

---

Nitpick comments:
In `@normalization/languages/portuguese/clitics.py`:
- Around line 9-26: The _CLITIC_SUFFIXES tuple in clitics.py contains a
duplicate "lhes" entry, so remove the repeated value and keep only one
occurrence. Update the _CLITIC_SUFFIXES definition directly to preserve the
intended suffix list and keep the regex alternation clear and maintainable.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bd9e175d-0105-472e-9fa5-96e2df635674

📥 Commits

Reviewing files that changed from the base of the PR and between 824db67 and d115d4d.

⛔ Files ignored due to path filters (1)
  • tests/e2e/files/gladia-3/pt.csv is excluded by !**/*.csv
📒 Files selected for processing (9)
  • normalization/languages/__init__.py
  • normalization/languages/portuguese/__init__.py
  • normalization/languages/portuguese/clitics.py
  • normalization/languages/portuguese/number_normalizer.py
  • normalization/languages/portuguese/operators.py
  • normalization/languages/portuguese/replacements.py
  • normalization/languages/portuguese/sentence_replacements.py
  • tests/unit/languages/portuguese_number_normalizer_test.py
  • tests/unit/languages/portuguese_operators_test.py

Comment on lines +40 to +45
# Meaningful affirmatives / idioms must survive normalization
assert pipeline.normalize("claro estou") == pipeline.normalize("claro estou")
assert pipeline.normalize("esta tudo bem") == pipeline.normalize("esta tudo bem")
assert pipeline.normalize("e pronto foi assim") == pipeline.normalize(
"e pronto foi assim"
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Tautological assertions provide no verification.

Lines 41, 42, and 43–45 compare pipeline.normalize(x) with pipeline.normalize(x) using the same input on both sides. Since normalize is deterministic, these reduce to x == x and will always pass — even if "claro", "bem", or "pronto" were incorrectly stripped as fillers. The comment at Line 40 states the intent ("Meaningful affirmations / idioms must survive normalization") but the tests do not verify that.

🐛 Proposed fix: assert against expected output
     # Meaningful affirmatives / idioms must survive normalization
-    assert pipeline.normalize("claro estou") == pipeline.normalize("claro estou")
-    assert pipeline.normalize("esta tudo bem") == pipeline.normalize("esta tudo bem")
-    assert pipeline.normalize("e pronto foi assim") == pipeline.normalize(
-        "e pronto foi assim"
-    )
+    assert pipeline.normalize("claro estou") == "claro estou"
+    assert pipeline.normalize("esta tudo bem") == "esta tudo bem"
+    assert pipeline.normalize("e pronto foi assim") == "e pronto foi assim"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Meaningful affirmatives / idioms must survive normalization
assert pipeline.normalize("claro estou") == pipeline.normalize("claro estou")
assert pipeline.normalize("esta tudo bem") == pipeline.normalize("esta tudo bem")
assert pipeline.normalize("e pronto foi assim") == pipeline.normalize(
"e pronto foi assim"
)
# Meaningful affirmatives / idioms must survive normalization
assert pipeline.normalize("claro estou") == "claro estou"
assert pipeline.normalize("esta tudo bem") == "esta tudo bem"
assert pipeline.normalize("e pronto foi assim") == "e pronto foi assim"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/languages/portuguese_operators_test.py` around lines 40 - 45, The
assertions in portuguese_operators_test.py are tautological because
`pipeline.normalize(...)` is compared against itself, so they never verify that
meaningful affirmatives/idioms survive normalization. Update the tests around
the existing `pipeline.normalize` calls to compare each input string against an
explicit expected normalized result (or an expected output fixture), using the
same test cases for “claro estou”, “esta tudo bem”, and “e pronto foi assim” so
the `normalize` behavior is actually validated.

Introduce a dedicated pt language pack with colloquial variants, clitic
merging, written-number expansion, and Orbio call-center oriented rules
(fillers, oral prepositions, gender/spelling variants, brand names).

Co-authored-by: Cursor <cursoragent@cursor.com>
@egenthon-cmd egenthon-cmd force-pushed the feat/portuguese-normalization branch from 6cc7867 to b3361e5 Compare July 9, 2026 18:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant