Skip to content
HeapyPublic

About

Unicode ICU4J port to Common Kotlin (work in progress)

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

ICU4K

Incremental port of Unicode ICU4J 78.3 to Common Kotlin.

Work in progress: this is not yet a replacement for ICU4J. Algorithms belong in commonMain; ICU4J may be used only as a JVM test oracle. Targets: JVM, JavaScript/Node, Linux x64 and macOS arm64. Toolchain: Kotlin 2.4.21, Gradle 9.8.1; JDK 25 to run Gradle.

./gradlew jvmTest jsNodeTest
# On Apple Silicon:
./gradlew macosArm64Test

Pinned ICU data is embedded for JVM, JS and Native. To verify generated data:

python3 scripts/generate-data.py --check

See porting scope for measured source size and stages. See the ICU4J bug tracker for upstream findings and reproductions. Unicode's upstream license and third-party notices are retained in LICENSE.

Common Kotlin bidirectional text is available through Bidi and BidiTransform:

import io.github.heapy.icu4k.text.Bidi

val bidi = Bidi()
bidi.setPara("abc אבג", 0, null)
println(bidi.writeReordered(0)) // abc גבא
val visualToLogical = bidi.getVisualMap()

Common Kotlin normalization is available through Normalizer2:

import io.github.heapy.icu4k.text.Normalizer2

val composed = Normalizer2.getNFCInstance().normalize("e\u0301") // é
val decomposed = Normalizer2.getNFDInstance().normalize("é") // e + U+0301

Common Kotlin transliteration supports registered ICU transforms and custom rules:

import io.github.heapy.icu4k.text.Transliterator

val ascii = Transliterator.getInstance("Latin-ASCII").transliterate("Crème brûlée")
// Creme brulee
val rules = Transliterator.createFromRules("example", "ab > X;", Transliterator.FORWARD)
val result = rules.transliterate("abab") // XX

Common Kotlin break rules can be compiled and used directly:

import io.github.heapy.icu4k.text.BreakIterator
import io.github.heapy.icu4k.text.RuleBasedBreakIterator

val iterator = RuleBasedBreakIterator("[a-z]+{200};[0-9]+{100};.;")
iterator.setText("abc12!")
while (iterator.next() != BreakIterator.DONE) {
    println("${iterator.current()}: ${iterator.getRuleStatus()}")
}
// Boundaries/statuses: 3/200, 5/100, 6/0.
val binaryRules: ByteArray = RuleBasedBreakIterator.compileRules("[a-z]+;.;")

The locale identity and language-tag core is available through ULocale:

import io.github.heapy.icu4k.util.ULocale

val locale = ULocale.forLanguageTag("en-US-u-ca-japanese")
println(locale.getName()) // en_US@calendar=japanese
println(locale.toLanguageTag()) // en-US-u-ca-japanese
val available = ULocale.getAvailableLocalesByType(ULocale.AvailableType.WITH_LEGACY_ALIASES)

ULocale display names and remaining locale services are still being ported.

Common Kotlin plural rules include locale data and custom rule descriptions:

import io.github.heapy.icu4k.text.PluralRules
import io.github.heapy.icu4k.util.ULocale

val plurals = PluralRules.forLocale(ULocale("en"))
println(plurals.select(1.0)) // one
println(plurals.select(1.0, 1, 0)) // other: visible fraction "1.0"
val custom = PluralRules.parseDescription("one: n = 1; few: n = 2..4")
println(custom.select(3.0)) // few

Fluent number formatting and formatted-number results are available in Common Kotlin:

import io.github.heapy.icu4k.number.NumberFormatter
import io.github.heapy.icu4k.number.Precision
import io.github.heapy.icu4k.util.BigDecimal
import io.github.heapy.icu4k.util.ULocale

val formatter = NumberFormatter.with()
    .precision(Precision.fixedFraction(2))
    .locale(ULocale.ENGLISH)
println(formatter.format(BigDecimal("12.345"))) // 12.34

val mixed = NumberFormatter.forSkeleton("unit/foot-and-inch unit-width-full-name")
    .locale(ULocale.ENGLISH)
println(mixed.format(1.5)) // 1 foot, 6 inches

FormattedNumber exports fields, attributed runs, rounded decimal values and resolved unit metadata. toFormat() produces a Java Format on JVM and a portable adapter on JS/Native; the Common append overload uses StringBuilder. Nested enum names in NumberFormatter are Kotlin aliases for the existing standalone enums. JVM java.util.Locale entry points are Kotlin extensions. Localized NumberFormat factories and the remaining ICU services are still being ported; locale resource coverage is still incremental.

Numeric ranges are available through NumberRangeFormatter, including field spans and identity/collapse policies:

import io.github.heapy.icu4k.number.NumberRangeFormatter
import io.github.heapy.icu4k.number.UnitWidth
import io.github.heapy.icu4k.util.MeasureUnit

val ranges = NumberRangeFormatter.withLocale(ULocale.ENGLISH)
    .numberFormatterBoth(NumberFormatter.with()
        .unit(MeasureUnit.METER)
        .unitWidth(UnitWidth.FULL_NAME))
println(ranges.formatRange(1, 2)) // 1–2 meters

ICU binary bundles support typed lookup, aliases and locale/path fallback:

import io.github.heapy.icu4k.impl.ICUData
import io.github.heapy.icu4k.impl.ICUResourceBundle

val bundle = ICUResourceBundle.getBundleInstance(
    ICUData.ICU_BRKITR_BASE_NAME, "root", disableFallback = true,
)
println(bundle.getWithFallback("dictionaries/Mymr").getString()) // burmesedict.dict

Locale availability lists come from the pinned ICU indexes; not all listed locale payloads are embedded yet. The embedded subset is listed in data/manifest.json. ICUResourceLoader can supply additional binary bundles on every target. UResourceBundle.getBundleInstance selects ICU or Java-style bundles from the root bundle type and preserves ICU4J's cache and fallback rules. ICUResourceBundle.getFullLocaleNameSet supports resource visitation, list-file and index fallback; JVM callers can use ICUClassLoaderResourceLoader for directories and JAR resources.

ICU CmnD .dat packages can be opened in Common code with ICUDataPackage.open(BinaryBuffer(datBytes)). The package exposes item lookup and asResourceLoader() for binary bundles. It borrows the supplied storage; keep the validated package buffer's position and bounds unchanged. ICUBinary.dataPath is scanned once during ICUBinary initialization; external files and packages precede embedded resources. JVM uses a system property; Native and Node use the environment variable com.ibm.icu.impl.ICUBinary.dataPath. JVM and Native retain mmap storage; Node retains file descriptors for live reads. Browser JS uses embedded or caller-supplied resources.

URLHandler shares file/archive guide rules in Common code. forFile(path) uses the host filesystem; forArchive(prefix, entries) uses a caller-supplied archive iterator. On JVM, the URLHandler.get(URL) extension resolves file/JAR/wsjar URLs and custom static factories from urlhandler.props; ICUClassLoaderResourceLoader uses that registry when enumerating resources. Java factories return the ported URLHandler type and expose get(java.net.URL).

ResourceBundleWrapper.getBundleInstance(baseName, localeID, loader, disableFallback) loads Java-style bundles through Common object/property providers. On JVM, ICUClassLoaderResourceLoader also loads actual Java resource-bundle classes and classpath .properties. The default ICU loader includes the pinned root and pool bundles. ULocale defaults and DISPLAY/FORMAT categories are mutable on every target; JVM defaults synchronize with java.util.Locale. JVM extensions forLocale and toLocale bridge Java locale identities. JVM resource extensions accept Java Locale and ClassLoader, expose getLocale(), and provide ICUResourceBundle's Java locale availability lists. Import the corresponding util/impl extensions. Remaining locale services and Java callable facade compatibility are still being ported.

ULocale.addLikelySubtags and ULocale.minimizeSubtags use the Common CLDR trie engine and pinned langInfo.res. For example, ULocale.addLikelySubtags(ULocale("en")) is en_Latn_US. Both minimization strategies preserve variants and keywords. ULocale.canonicalize and ULocale.createCanonical apply CLDR language, script, territory, variant, subdivision and transformed-extension aliases from metadata. For example, ULocale.createCanonical("sh_RS") is sr_Latn_RS.

Region provides pinned territory IDs, numeric/three-letter aliases, deprecated preferred values, and geographic/grouping containment in Common code. For example, Region.getInstance("IT").getContainingRegion(Region.RegionType.CONTINENT) is 150.

LocaleData exposes exemplar sets, quotation delimiters, display patterns, measurement systems, paper sizes and the CLDR version. Core and language bundles for English, French, Arabic and Japanese are embedded with their root/pool data. The complete locale payload set is still being ported.

SimpleFormatter compiles ICU numbered patterns and supports apostrophe quoting, argument offsets and append/replace formatting, including use of the result builder as an argument during replacement. Example: SimpleFormatter.compile("{1} in {0}").

FormattedStringBuilder supplies centered UTF-16 edits with per-code-unit fields, and SimpleFormatterImpl.formatPrefixSuffix applies compiled wrappers to a span. FormatField uses Java Format.Field on JVM; extracted NumberFormatField identities support numeric annotations while the full NumberFormat implementation is pending.

ConstrainedFieldPosition carries field/class/value constraints and iteration state in Common code. UFieldPosition retains fraction-digit metadata; its JVM base is java.text.FieldPosition, with portable index/attribute carriers on JS and Native. Formatted-value span iteration and exports are still being ported.

StaticUnicodeSets loads frozen CLDR parse symbols, separator/digit unions and the formatting trim sets from Common resources. These support upcoming span iteration and number parsing.

ICU's legacy decimal arithmetic is also available in Common Kotlin:

import io.github.heapy.icu4k.math.BigDecimal
import io.github.heapy.icu4k.math.MathContext

val result = BigDecimal("1").divide(BigDecimal("7"), MathContext(9))
println(result) // 0.142857143

math.BigDecimal preserves ICU contexts and representation-sensitive equality; util.BigDecimal is the separate standard decimal interchange type.

The legacy DecimalFormat API is available with explicit symbols:

import io.github.heapy.icu4k.text.DecimalFormat
import io.github.heapy.icu4k.text.DecimalFormatSymbols
import io.github.heapy.icu4k.util.ULocale

val decimal = DecimalFormat("#,##0.00", DecimalFormatSymbols(ULocale("en_US")))
println(decimal.format(1234.5)) // 1,234.50
println(decimal.parse("1,234.50")) // 1234.5

RuleBasedNumberFormat exposes the common rule engine and predefined ICU rule data:

import io.github.heapy.icu4k.text.RuleBasedNumberFormat
import io.github.heapy.icu4k.util.ULocale

val words = RuleBasedNumberFormat(ULocale.ENGLISH, RuleBasedNumberFormat.SPELLOUT)
println(words.format(1234L)) // one thousand two hundred thirty-four
println(words.parse("twenty-one")) // 21

Custom descriptions, named rule sets, localized names, capitalization, rounding, large-number dispatch and shallow cloning are supported. JVM object streams use the source custom serialization protocol. JVM Locale conveniences are Kotlin factory functions; Java constructor/typed-overload parity is still incremental. Native subclasses follow the existing typed-clone convention.

The default collation-backed lenient scanner is available on all targets. Enable it with words.setLenientParseMode(true) to accept primary-equivalent spellings and the locale's lenient separators. Custom RbnfLenientScannerProvider implementations can still be supplied. ICU data outside the embedded subset is still being ported.

MessagePattern parses message, choice, plural and select patterns in Common Kotlin:

import io.github.heapy.icu4k.text.MessagePattern

val message = MessagePattern("{n,plural,one{# file}other{# files}}")
for (i in 0..<message.countParts()) {
    println(message.getPart(i))
}

The parser exposes syntax parts and nested-message boundaries. MessageFormat formats and parses numbered or named arguments, including nested choices, plurals and selects:

import io.github.heapy.icu4k.text.MessageFormat

val files = MessageFormat("{n,plural,one{# file} other{# files}}", ULocale.US)
println(files.format(mapOf("n" to 2))) // 2 files

Custom formats, attributed output, cloning and ICU4J-compatible JVM streams are supported. JVM callers may also supply java.text.Format implementations.

SelectFormat selects a message by keyword and falls back to other:

import io.github.heapy.icu4k.text.SelectFormat

val selection = SelectFormat("female{She} male{He} other{They}")
println(selection.format("female")) // She
println(selection.format("unknown")) // They

Nested message arguments are returned as pattern text, as in ICU4J.

Localized NumberFormat factories also run in Common Kotlin:

import io.github.heapy.icu4k.text.NumberFormat
import io.github.heapy.icu4k.util.ULocale

val decimal = NumberFormat.getNumberInstance(ULocale("en_US"))
val roman = NumberFormat.getInstance(ULocale("en@numbers=roman"))
println(decimal.format(1234.5)) // 1,234.5
println(roman.format(2026L))   // MMXXVI

Plural messages are available through PluralFormat:

import io.github.heapy.icu4k.text.PluralFormat

val files = PluralFormat(ULocale.ENGLISH, "one{# file} other{# files}")
println(files.format(2.0)) // 2 files

Locale-sensitive string casing is available through UCharacter:

import io.github.heapy.icu4k.lang.UCharacter

println(UCharacter.toUpperCase(ULocale("tr"), "istanbul")) // İSTANBUL
println(UCharacter.toTitleCase(ULocale("nl"), "ijsselmeer", null)) // IJsselmeer

Rule-based collation is available through the public Common Kotlin API:

import io.github.heapy.icu4k.text.RuleBasedCollator

val collator = RuleBasedCollator("&c<ch<d")
println(collator.compare("ch", "d")) // -1
collator.setNumericCollation(true)
println(collator.compare("2", "10")) // -1
val key = collator.getCollationKey("ch")!!
val elements = collator.getCollationElementIterator("ch")

The rule constructor, attributes, sort keys, CE iteration, freeze and cloning are implemented. Locale factories, service registration and locale enumeration are also available:

import io.github.heapy.icu4k.text.Collator
import io.github.heapy.icu4k.util.ULocale

val german = Collator.getInstance(ULocale("de@collation=phonebook"))
val numeric = Collator.getInstance(ULocale("en@colNumeric=yes"))
println(numeric.compare("2", "10")) // -1

setReorderCodes accepts both a nullable IntArray and Kotlin varargs; the JVM vararg convenience is named setReorderCodesValues, while the array method retains setReorderCodes.

Locale-sensitive substring search is available through StringSearch:

import io.github.heapy.icu4k.text.StringSearch
import io.github.heapy.icu4k.text.StringCharacterIterator

val search = StringSearch("ana", StringCharacterIterator("banana"), german)
search.setOverlapping(true)
println(search.first()) // 1
println(search.next()) // 3

After changing a search's shared collator, call search.reset() before searching again, as required by ICU4J's StringSearch contract. Known source edge cases and verification limits are recorded in the porting log.

AlphabeticIndex groups records using locale-sensitive labels and collation:

import io.github.heapy.icu4k.text.AlphabeticIndex

val index = AlphabeticIndex<Int>(ULocale.ENGLISH)
index.addRecord("Alice", 1).addRecord("Bob", 2)
val snapshot = index.buildImmutableIndex() // labels and boundaries, without records
val bucket = snapshot.getBucket(snapshot.getBucketIndex("Bob"))!!
println(bucket.getLabel()) // B

LocalePriorityList parses Accept-Language priorities and supports reusable builders:

import io.github.heapy.icu4k.util.LocalePriorityList

val priorities = LocalePriorityList.add("en, fr;q=0.7, de;q=0.5").build(true)
println(priorities.getWeight(ULocale.FRENCH)) // 0.7

Time-zone rules and transitions are available in Common Kotlin:

import io.github.heapy.icu4k.util.AnnualTimeZoneRule
import io.github.heapy.icu4k.util.DateTimeRule

val dst = AnnualTimeZoneRule(
    "DST", -18_000_000, 3_600_000,
    DateTimeRule(2, 2, 1, 7_200_000, DateTimeRule.WALL_TIME),
    2007, AnnualTimeZoneRule.MAX_YEAR,
)
println(dst.getStartInYear(2024, -18_000_000, 0)!!.getTime()) // 1710054000000

TimeZoneFormat formats and parses all named, localized-GMT and ISO styles using real timezone history. Frozen locale factories, custom name providers, GMT configuration, clone behavior and source-compatible JVM serialization are included.

TimeZoneNames exposes localized CLDR names, historical metazone mappings, prefix matches and region-sensitive TZDB abbreviations. Locale factories, custom JVM factories and source-compatible JVM serialization are included.

DateFormat provides the abstract calendar-based format/parse protocol, date fields, leniency/context controls and JVM temporal-input conversion. A concrete pattern formatter and style factories are still being ported.

DateFormatSymbols exposes locale/calendar era, month, weekday, quarter, AM/PM and leap-month names, with ICU's clone and defensive-copy behavior. For example, DateFormatSymbols(ULocale("fr_FR")).getMonths()[0] is "janvier". Lazy zone-name tables and explicit calendar/class constructors are also available. JVM serialization preserves ICU's field schema and shared symbol-array graph.

GregorianCalendar supports default or explicit time zones, locale timezone keywords, date-field constructors, field resolution and date arithmetic:

import io.github.heapy.icu4k.util.Calendar
import io.github.heapy.icu4k.util.GregorianCalendar
import io.github.heapy.icu4k.util.SimpleTimeZone
import io.github.heapy.icu4k.util.ULocale

val calendar = GregorianCalendar(SimpleTimeZone(0, "UTC"), ULocale.US)
calendar.clear()
calendar.set(2024, Calendar.JANUARY, 31)
calendar.add(Calendar.MONTH, 1)
println(calendar.get(Calendar.DATE)) // 29

BuddhistCalendar, TaiwanCalendar, IndianCalendar, CopticCalendar, EthiopicCalendar, HebrewCalendar and the ICU-internal, deprecated PersianCalendar also support default/locale/Date/numeric constructors, explicit zones, field resolution, date arithmetic and Native copies.

JapaneseCalendar also supports default/locale/date constructors and the ICU era boundaries, including accession-month/day defaults for year one:

import io.github.heapy.icu4k.util.JapaneseCalendar

val japanese = JapaneseCalendar(ULocale("ja@timezone=Asia/Tokyo"))
japanese.clear()
japanese.set(Calendar.ERA, JapaneseCalendar.REIWA)
japanese.set(Calendar.YEAR, 1)
println(japanese.get(Calendar.MONTH)) // Calendar.MAY
println(japanese.get(Calendar.DATE)) // 1

IslamicCalendar supports all four ICU calculation modes: astronomical Islamic, civil, tabular with Thursday epoch, and Umm al-Qura. Locale constructors select the calendar keyword; date and numeric-field constructors retain ICU's civil default. setCivil preserves the instant while recomputing calendar fields.

ChineseCalendar and the ICU-internal DangiCalendar support lunisolar fields, leap-month codes, ordinal months, month arithmetic and independent calendar copies. Astronomical calculations retain ICU's separate China/Korea zones, including the historical Korean offset changes.

Calendar.getInstance(ULocale("th_TH")) selects the locale's preferred calendar. Explicit calendar keywords select every ICU calendar type; unknown types fall back to Gregorian. The factory also supports explicit time zones and ISO 8601 week rules, including locale first-day and region overrides.

System zones and default-zone lookup use the embedded ICU transition data:

import io.github.heapy.icu4k.util.TimeZone

val zone = TimeZone.getTimeZone("America/New_York")
println(zone.getOffset(1710054000000L)) // -14400000
val defaultZone = TimeZone.getDefault() // independent mutable copy
val frozen = TimeZone.getFrozenTimeZone("Europe/Minsk")

JVM also supports TIMEZONE_JDK and default synchronization with java.util.TimeZone. JS/Native use ICU rules and process-local library default replacement; they discover the initial system zone through the host platform. Explicit JDK-mode construction requires JVM.

SimpleTimeZone also supports recurring daylight-saving rules, transitions and local-time disambiguation through BasicTimeZone.LocalOption. The Date boundary retains mutable epoch milliseconds and aliases java.util.Date on JVM. RuleBasedTimeZone exposes historical and recurring transition rules. Calendar, SimpleTimeZone, RuleBasedTimeZone and OlsonTimeZone JVM streams preserve ICU4J's field schemas, algorithm settings and historical zone data.

SimpleDateFormat now formats and parses common date patterns:

import io.github.heapy.icu4k.text.SimpleDateFormat
import io.github.heapy.icu4k.util.Date

val format = SimpleDateFormat("yyyy-MM-dd HH:mm:ss XXX", ULocale.US)
format.setTimeZone(TimeZone.getTimeZone("Etc/UTC"))
println(format.format(Date(0))) // 1970-01-01 00:00:00 Z
println(format.parse("1970-01-01 00:00:00 Z")!!.getTime()) // 0

Calendar-specific style construction is available through calendar.getDateTimeFormat(dateStyle, timeStyle, locale). Pattern overrides, localized symbols, attributed text, century boundaries, cloning and JVM serialization are supported. DateFormat provides style and skeleton factories, including factories that adopt an explicit calendar:

import io.github.heapy.icu4k.text.DateFormat

val relative = DateFormat.getDateInstance(DateFormat.RELATIVE_SHORT, ULocale.US)
println(relative.format(Date())) // today
val month = DateFormat.getInstanceForSkeleton("yMMMM", ULocale.US)

Relative styles use localized day names and capitalization contexts, then fall back to ordinary dates outside the locale's relative-day range. Relative-date parsing throws the same unsupported-operation exception as ICU4J. See the porting log for current coverage.

DateTimePatternGenerator exposes locale-aware skeleton matching and custom patterns:

import io.github.heapy.icu4k.text.DateTimePatternGenerator

val generator = DateTimePatternGenerator.getInstance(ULocale.US)
println(generator.getBestPattern("yMMMd")) // MMM d, y
generator.addPattern("yyyy-MM-dd", true, DateTimePatternGenerator.PatternInfo())

The API includes field names, length options, mutable parser items and frozen or cloned instances. ICU4J 78.3's cache and clone behavior is preserved; details are recorded in the porting log.

DateInterval stores interval endpoints, while DateIntervalInfo loads and customizes CLDR interval patterns:

import io.github.heapy.icu4k.text.DateIntervalInfo
import io.github.heapy.icu4k.util.Calendar
import io.github.heapy.icu4k.util.DateInterval
import io.github.heapy.icu4k.util.ULocale

val interval = DateInterval(0, 86_400_000)
val patterns = DateIntervalInfo(ULocale.US)
val parts = patterns.getIntervalPattern("yMMMd", Calendar.DATE)
println(parts?.getFirstPart())

Interval pattern data supports endpoint order, quoted pattern splitting, skeleton matching, copy-on-write clones and ICU4J-compatible JVM streams. DateIntervalFormat formats compact CLDR ranges and exposes date fields and endpoint spans through FormattedValue:

import io.github.heapy.icu4k.text.DateIntervalFormat
import io.github.heapy.icu4k.util.DateInterval
import io.github.heapy.icu4k.util.TimeZone
import io.github.heapy.icu4k.util.ULocale

val formatter = DateIntervalFormat.getInstance("yMMMd", ULocale.US)
formatter.setTimeZone(TimeZone.GMT_ZONE)
val value = formatter.formatToValue(DateInterval(1_705_323_723_456, 1_705_410_123_456))
println(value) // Jan 15 – 16, 2024

It supports custom interval patterns, calendars, time zones, capitalization, cloning and JVM stream compatibility. Parsing is unsupported, as in ICU4J.

The common impl.DateNumberFormat now provides ICU's date-specific integer formatting and parsing, with localized digits and source-compatible cache and numeric boundary behavior. JVM serialization interoperates with ICU4J 78.3.

The common legacy MeasureFormat API formats quantities, per-unit quantities and unit lists, including numeric hour/minute/second durations:

import io.github.heapy.icu4k.text.MeasureFormat
import io.github.heapy.icu4k.util.Measure
import io.github.heapy.icu4k.util.MeasureUnit
import io.github.heapy.icu4k.util.ULocale

val measures = MeasureFormat.getInstance(ULocale.US, MeasureFormat.FormatWidth.WIDE)
println(measures.formatMeasures(Measure(70, MeasureUnit.FOOT), Measure(5.3, MeasureUnit.INCH)))
// 70 feet, 5.3 inches

val duration = MeasureFormat.getInstance(ULocale.US, MeasureFormat.FormatWidth.NUMERIC)
println(duration.formatMeasures(Measure(5, MeasureUnit.HOUR), Measure(7.25, MeasureUnit.SECOND)))
// 5:00:07.25

MeasureFormat.getCurrencyFormat formats and parses currency amounts. The deprecated TimeUnitFormat also parses time-unit amounts using both full and abbreviated patterns. These APIs retain ICU4J 78.3's cache, field-position and locale-mutation behavior; JVM serialization uses the source proxy protocol.

ArabicShaping performs character-based shaping for legacy Arabic text formats, including contextual letters, Lam–Alef ligatures and contextual Arabic digits:

import io.github.heapy.icu4k.text.ArabicShaping

val shaping = ArabicShaping(ArabicShaping.LETTERS_SHAPE)
println(shaping.shape("لا")) // ﻻ (U+FEFB)

String, buffer-slice, in-place and size-query APIs preserve ICU4J's option and fixed-length behavior. Expansion without enough room throws ArabicShapingException.

RelativeDateTimeFormatter formats numeric and qualitative relative dates with CLDR long, short and narrow styles:

import io.github.heapy.icu4k.text.RelativeDateTimeFormatter
import io.github.heapy.icu4k.util.ULocale

val relative = RelativeDateTimeFormatter.getInstance(ULocale.US)
println(relative.format(-1.0, RelativeDateTimeFormatter.RelativeDateTimeUnit.DAY)) // yesterday
println(relative.formatNumeric(3.0, RelativeDateTimeFormatter.RelativeDateTimeUnit.DAY)) // in 3 days
val value = relative.formatToValue(3.0, RelativeDateTimeFormatter.RelativeDateTimeUnit.DAY)

Formatted results expose literal, numeric and number fields. The API also supports custom number formats, capitalization and locale-specific date/time combination.

LocaleMatcher negotiates supported locales using CLDR language, script and region distances:

import io.github.heapy.icu4k.util.LocaleMatcher
import io.github.heapy.icu4k.util.ULocale

val matcher = LocaleMatcher.builder()
    .setSupportedLocales("en_GB, en_US, fr_FR")
    .build()
println(matcher.getBestMatch(ULocale("en_AU"))) // en_GB
val result = matcher.getBestMatchResult(ULocale("fr_CA@numbers=arab"))
println(result.makeResolvedULocale()) // fr_CA@numbers=arab

Builders configure defaults, script/language preference, desired-list demotion, matching direction and maximum distance. Results retain original locale indices and merge desired region, variant and extensions into the supported locale. ULocale.acceptLanguage provides the corresponding HTTP-header and array wrappers with the ICU4J fallback flag. JVM extensions also accept java.util.Locale.

About

Unicode ICU4J port to Common Kotlin (work in progress)

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages