diff --git a/README.md b/README.md index 74d5eb0..b16a2fe 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,7 @@ HumanReadable.timeAgo(instant) // "il y a 3 jours" ### Supported languages +* Arabic (since 1.13.0) * Czech * Chinese (since 1.3.0) * Dutch diff --git a/demo/src/webMain/kotlin/Language.kt b/demo/src/webMain/kotlin/Language.kt index 657aec9..a519aaa 100644 --- a/demo/src/webMain/kotlin/Language.kt +++ b/demo/src/webMain/kotlin/Language.kt @@ -1,5 +1,6 @@ internal enum class Language(val code: String) { English("en"), + Arabic("ar"), Czech("cs"), Chinese("zh"), Dutch("nl"), diff --git a/src/appleAndJsMain/kotlin/nl/jacobras/humanreadable/extendLibresPlurals.kt b/src/appleAndJsMain/kotlin/nl/jacobras/humanreadable/extendLibresPlurals.kt index 759b096..732d740 100644 --- a/src/appleAndJsMain/kotlin/nl/jacobras/humanreadable/extendLibresPlurals.kt +++ b/src/appleAndJsMain/kotlin/nl/jacobras/humanreadable/extendLibresPlurals.kt @@ -73,4 +73,15 @@ internal actual fun extendLibresPlurals() { else -> PluralForm.Other } } + + PluralRules["ar"] = PluralRule { number -> + when { + number == 0 -> PluralForm.Zero + number == 1 -> PluralForm.One + number == 2 -> PluralForm.Two + number % 100 in 3..10 -> PluralForm.Few + number % 100 in 11..99 -> PluralForm.Many + else -> PluralForm.Other + } + } } \ No newline at end of file diff --git a/src/commonMain/kotlin/nl/jacobras/humanreadable/HumanReadableDuration.kt b/src/commonMain/kotlin/nl/jacobras/humanreadable/HumanReadableDuration.kt index eff5915..9f38aab 100644 --- a/src/commonMain/kotlin/nl/jacobras/humanreadable/HumanReadableDuration.kt +++ b/src/commonMain/kotlin/nl/jacobras/humanreadable/HumanReadableDuration.kt @@ -1,5 +1,6 @@ package nl.jacobras.humanreadable +import io.github.skeptick.libres.LibresSettings import kotlin.math.roundToInt import kotlin.time.Duration @@ -19,26 +20,43 @@ internal fun formatDuration( return when { secondsAgo < 60 -> { - "$secondsAgo ${TimeUnit.Seconds.format(secondsAgo, relativeTime)}" + formatUnit(secondsAgo, TimeUnit.Seconds, relativeTime) } secondsAgo < 3600 -> { val minutes = duration.inWholeMinutes.toInt() - "$minutes ${TimeUnit.Minutes.format(minutes, relativeTime)}" + formatUnit(minutes, TimeUnit.Minutes, relativeTime) } daysAgo < 1 -> { - "$hoursAgo ${TimeUnit.Hours.format(hoursAgo, relativeTime)}" + formatUnit(hoursAgo, TimeUnit.Hours, relativeTime) } daysAgo < 7 -> { - "$daysAgo ${TimeUnit.Days.format(daysAgo, relativeTime)}" + formatUnit(daysAgo, TimeUnit.Days, relativeTime) } daysAgo < 30 -> { - "$weeksAgo ${TimeUnit.Weeks.format(weeksAgo, relativeTime)}" + formatUnit(weeksAgo, TimeUnit.Weeks, relativeTime) } monthsAgo < 12 || yearsAgo == 0 -> { - "$monthsAgo ${TimeUnit.Months.format(monthsAgo, relativeTime)}" + formatUnit(monthsAgo, TimeUnit.Months, relativeTime) } else -> { - "$yearsAgo ${TimeUnit.Years.format(yearsAgo, relativeTime)}" + formatUnit(yearsAgo, TimeUnit.Years, relativeTime) } } +} + +/** + * Formats a [count] with its [unit]. Normally produces "$count $unit", but for Arabic the + * singular (1) and dual (2) forms are encoded in the unit word itself, so the numeral is omitted. + */ +private fun formatUnit( + count: Int, + unit: TimeUnit, + relativeTime: RelativeTime +): String { + val unitText = unit.format(count, relativeTime) + return if (LibresSettings.languageCode == "ar" && (count == 1 || count == 2)) { + unitText + } else { + "$count $unitText" + } } \ No newline at end of file diff --git a/src/commonMain/libres/strings/data_units_ar.xml b/src/commonMain/libres/strings/data_units_ar.xml new file mode 100644 index 0000000..5a567c8 --- /dev/null +++ b/src/commonMain/libres/strings/data_units_ar.xml @@ -0,0 +1,8 @@ + + + ب + ك.ب + م.ب + ج.ب + ت.ب + diff --git a/src/commonMain/libres/strings/distance_units_ar.xml b/src/commonMain/libres/strings/distance_units_ar.xml new file mode 100644 index 0000000..54999bb --- /dev/null +++ b/src/commonMain/libres/strings/distance_units_ar.xml @@ -0,0 +1,7 @@ + + + م + كم + قدم + ميل + diff --git a/src/commonMain/libres/strings/time_units_ar.xml b/src/commonMain/libres/strings/time_units_ar.xml new file mode 100644 index 0000000..b03787e --- /dev/null +++ b/src/commonMain/libres/strings/time_units_ar.xml @@ -0,0 +1,111 @@ + + + + ثانية + ثانية + ثانيتان + ثوان + ثانية + ثانية + + + ثانيتين + + + ثانيتين + + + + دقيقة + دقيقة + دقيقتان + دقائق + دقيقة + دقيقة + + + دقيقتين + + + دقيقتين + + + + ساعة + ساعة + ساعتان + ساعات + ساعة + ساعة + + + ساعتين + + + ساعتين + + + + يوم + يوم + يومان + أيام + يوم + يوم + + + يومين + + + يومين + + + + أسبوع + أسبوع + أسبوعان + أسابيع + أسبوع + أسبوع + + + أسبوعين + + + أسبوعين + + + + شهر + شهر + شهران + أشهر + شهر + شهر + + + شهرين + + + شهرين + + + + سنة + سنة + سنتان + سنوات + سنة + سنة + + + سنتين + + + سنتين + + + قبل ${time} + بعد ${time} + الآن + diff --git a/src/commonTest/kotlin/nl/jacobras/humanreadable/localized/LocalizedTests.kt b/src/commonTest/kotlin/nl/jacobras/humanreadable/localized/LocalizedTests.kt index e9653cd..70f61b7 100644 --- a/src/commonTest/kotlin/nl/jacobras/humanreadable/localized/LocalizedTests.kt +++ b/src/commonTest/kotlin/nl/jacobras/humanreadable/localized/LocalizedTests.kt @@ -47,6 +47,32 @@ class LocalizedTests { private val oneYearAgo = now - oneYear private val oneYearFromNow = now + oneYear + @Test + fun ar_arabic() { + LibresSettings.languageCode = "ar" + assertThat(HumanReadable.duration(0.seconds)).isEqualTo("0 ثانية") + assertThat(HumanReadable.duration(1.seconds)).isEqualTo("ثانية") + assertThat(HumanReadable.duration(2.seconds)).isEqualTo("ثانيتان") + assertThat(HumanReadable.duration(3.seconds)).isEqualTo("3 ثوان") + assertThat(HumanReadable.duration(11.seconds)).isEqualTo("11 ثانية") + + assertThat(HumanReadable.timeAgo(now, baseInstant = now)).isEqualTo("الآن") + assertThat(HumanReadable.timeAgo(twoSecondsAgo, baseInstant = now)).isEqualTo("قبل ثانيتين") + assertThat(HumanReadable.timeAgo(oneMinuteFromNow, baseInstant = now)).isEqualTo("بعد دقيقة") + assertThat(HumanReadable.duration(3.days)).isEqualTo("3 أيام") + assertThat(HumanReadable.duration(twoMonths)).isEqualTo("شهران") + assertThat(HumanReadable.duration(oneYear)).isEqualTo("سنة") + + assertThat(HumanReadable.number(1_000_000.34, decimals = 2)).isEqualTo("1,000,000.34") + assertThat(HumanReadable.number(-4.34, decimals = 2)).isEqualTo("-4.34") + + assertThat(HumanReadable.fileSize(2_000_000, decimals = 1)).isEqualTo("1.9 م.ب") + + assertThat(HumanReadable.abbreviation(5_100_000, decimals = 1)).isEqualTo("5.1M") + + assertThat(HumanReadable.distance(7234, unit = DistanceUnit.Meter)).isEqualTo("7.2 كم") + } + @Test fun cs_czech() { LibresSettings.languageCode = "cs" @@ -307,4 +333,4 @@ class LocalizedTests { assertThat(HumanReadable.number(1_000_000.34, decimals = 2)).isEqualTo("1,000,000.34") assertThat(HumanReadable.number(-4.34, decimals = 2)).isEqualTo("-4.34") } -} \ No newline at end of file +}