text_normalization
This library provides extensible Unicode aware text cleaning and normalization for atoms, character lists, and character code lists. It implements Unicode 17.0.0 normalization and default casing independently of backend-specific Unicode normalization predicates.
API documentation
Open the ../../apis/library_index.html#text-normalization link in a web browser.
Loading
To load the library:
| ?- logtalk_load(text_normalization(loader)).
Testing
To test this library predicates:
| ?- logtalk_load(text_normalization(tester)).
To run the optimized Unicode conformance tests:
| ?- logtalk_load(text_normalization(tester_unicode_conformance)).
The conformance test suite checks all 20,034 vectors in Unicode 17.0.0
NormalizationTest.txt and all 1,585 full default case-fold mappings.
Usage
The text_normalizer(Representation, Profile) object accepts the
representations atom, chars, and codes. The profile must
implement the text_normalization_profile_protocol protocol. The
bundled default_text_normalization profile is language-neutral.
| ?- text_normalizer(codes, default_text_normalization)::normalize_unicode(
nfd, [197], Normalized
).
Normalized = [65, 778]
yes
| ?- text_normalizer(codes, default_text_normalization)::case_fold(
[83, 116, 114, 97, 223, 101], Folded
).
Folded = [115, 116, 114, 97, 115, 115, 101]
yes
| ?- text_normalizer(atom, default_text_normalization)::decode_entities(
'<A&B>', Decoded
).
Decoded = '<A&B>'
yes
| ?- text_normalizer(atom, default_text_normalization)::clean(
' FOO BAR ', Cleaned,
[case(lower)]
).
Cleaned = 'foo bar'
yes
Operations
normalize_unicode/3 implements NFC, NFD, NFKC, and NFKD, including
algorithmic Hangul decomposition and composition. case_fold/2
implements full default Unicode folding. Lowercase and uppercase
conversion use default Unicode mappings and unconditional special
casing; lowercase conversion also implements the language-independent
final-sigma rule. title_case/2 uses a simple boundary definition: a
non-cased, non-case-ignorable character starts a new word.
remove_diacritics/2 performs canonical decomposition, removes every
Unicode Mark-category character, and returns NFC. fold_diacritics/2
additionally applies profile mappings for characters such as ø and
ł that do not have a canonical decomposition. Mark removal and
transliteration are intentionally lossy operations and are distinct from
Unicode normalization.
decode_entities/2-3 accepts semicolon-terminated decimal and
hexadecimal numeric references and the case-sensitive names supplied by
the profile. The default profile includes XML’s five predefined entities
and all the semicolon-terminated named character references defined by
WHATWG HTML.
Options
decode_entities/3 accepts unknown(preserve|error), defaulting to
preserve.
normalize_whitespace/3 accepts:
trim(Boolean), defaulting totrue.collapse(none|horizontal|all), defaulting toall.line_endings(lf|crlf|cr|preserve), defaulting tolf.controls(preserve|remove), defaulting topreserve.
clean/3 accepts:
unicode(nfc|nfd|nfkc|nfkd), defaulting tonfc.entities(Boolean), defaulting totrue.unknown_entities(preserve|error), defaulting topreserve.case(preserve|fold|lower|upper|title), defaulting topreserve.diacritics(none|remove|fold), defaulting tonone.whitespace(Boolean), defaulting totrue.The four whitespace options listed above.
Duplicate options and options that do not apply to the called predicate are rejected. The fixed clean pipeline decodes entities, converts case, handles diacritics, applies the requested final Unicode normalization, and finally normalizes whitespace.
Adding profiles
A custom profile can extend default_text_normalization and override
only the required policy hooks while inheriting built-in mappings:
:- object(turkic_text_normalization,
extends(default_text_normalization)).
case_conversion(lower, [73], [305]) :-
!.
case_conversion(Mode, Codes, Converted) :-
^^case_conversion(Mode, Codes, Converted).
diacritic_fold(216, [79]) :-
!.
diacritic_fold(Code, Folded) :-
^^diacritic_fold(Code, Folded).
named_entity(example, [88]) :-
!.
named_entity(Name, Codes) :-
^^named_entity(Name, Codes).
:- end_object.
Unicode normalization itself is deliberately not overridable. Profile case overrides receive and return complete code lists, while diacritic and entity hooks operate on numeric code points.
Spelling correction is outside the core facade because it requires an
application-specific language model or dictionary. Providers can
implement the separate spelling_normalizer_protocol protocol and
compose correction with this library.
Unicode data and portability
The Unicode 17.0.0 normalization, casing, character-property data, and
normalization conformance vectors are owned by the unicode_data
library and loaded as a dependency. The static XML and WHATWG HTML
entity table remains local to this library and contains only ASCII
source text and numeric character values.
The codes representation supports every Unicode scalar value on all
supported backends. The atom and chars representations can only
round-trip characters supported by the backend’s native character
representation; use codes when processing the complete scalar range
portably.
The library implements Unicode normalization and default casing, not
grapheme segmentation, locale negotiation, encoding conversion, or full
HTML parsing. Use character_sets for encoding conversion and
tokenization for downstream text segmentation.