Katakanlah saya memiliki periode hari:

Period p = Period.ofDays(3);

Dan saya ingin memformat periode dengan label "hari" dalam string untuk mendapatkan string ini sebagai output:

// "3 days"

...tetapi, saya ingin melokalkan komponen "hari", jadi saya tidak dapat menggunakan string yang diformat seperti berikut, jika tidak maka hanya akan muncul dengan benar dalam bahasa Inggris:

String.format("%d days", numberOfDays); // Won't localize 'days'

API apa yang ada di Java/Kotlin/Android untuk mewakili periode waktu, seperti jam, hari, minggu, tahun di suatu lokal? Saya lebih suka tidak melokalkan kata-kata itu sendiri jika saya bisa membiarkan API melakukannya.

2
Benstrom 18 Maret 2020, 00:43

2 jawaban

Jawaban Terbaik

Seperti yang disarankan dalam satu komentar, Anda dapat menggunakan lib saya Time4J dan menggunakan kode berikut:

Period p = Period.ofDays(3);
Locale loc = Locale.ENGLISH; // or any other supported locale
String formatted = PrettyTime.of(loc).print(p); // 3 days

Tutorial ini juga berisi daftar bahasa yang saat ini didukung. Omong-omong, Time4J-ekuivalen untuk java.time.Period akan menjadi net.time4j.Duration<CalendarUnit> jika Anda tertarik dengan fitur lain seperti normalisasi atau kompatibilitas ISO yang diperluas, dll.

Catatan: Jika Anda menggunakan Android maka sebaiknya gunakan perpustakaan saudara Time4A alih-alih Time4J tetapi kode yang disajikan akan sama.

2
Meno Hochschild 8 April 2020, 12:34

Saat menggunakan jenis Duration Kotlin dengan pemformatan lokal, karena saya tidak dapat menemukan solusi yang baik, saya menulisnya sendiri. Ini didasarkan pada API yang disediakan mulai dari Android 9 (untuk unit yang dilokalkan), tetapi dengan fallback ke unit bahasa Inggris untuk versi Android yang lebih rendah sehingga dapat digunakan dengan aplikasi penargetan yang lebih rendah.

Berikut tampilannya dari sisi penggunaan (lihat Kotlin Durasi< /a> ketik untuk memahami baris pertama):

val duration = 5.days.plus(3.hours).plus(2.minutes).plus(214.milliseconds)

DurationFormat().format(duration) // "5day 3hour 2min"
DurationFormat(Locale.GERMANY).format(duration) // "5T 3Std. 2Min."
DurationFormat(Locale.forLanguageTag("ar").format(duration) // "٥يوم ٣ساعة ٢د"
DurationFormat().format(duration, smallestUnit = DurationFormat.Unit.HOUR) // "5day 3hour"
DurationFormat().format(15.minutes) // "15min"
DurationFormat().format(0.hours) // "0sec"

Seperti yang Anda lihat, Anda dapat menentukan locale khusus untuk jenis DurationFormat. Secara default menggunakan Locale.getDefault(). Bahasa yang memiliki simbol angka yang berbeda dari romanic juga didukung (melalui NumberFormat). Selain itu, Anda dapat menentukan smallestUnit khusus, secara default disetel ke SECOND, sehingga milidetik tidak akan ditampilkan. Perhatikan bahwa setiap unit dengan nilai 0 akan diabaikan dan jika seluruh bilangan adalah 0, unit terkecil akan digunakan dengan nilai 0.

Ini adalah jenis DurationFormat lengkap untuk menyalin & menempel (juga tersedia sebagai GitHub Gist termasuk pengujian unit):

import android.icu.text.MeasureFormat
import android.icu.text.NumberFormat
import android.icu.util.MeasureUnit
import android.os.Build
import java.util.Locale
import kotlin.time.Duration
import kotlin.time.ExperimentalTime
import kotlin.time.days
import kotlin.time.hours
import kotlin.time.milliseconds
import kotlin.time.minutes
import kotlin.time.seconds

@ExperimentalTime
data class DurationFormat(val locale: Locale = Locale.getDefault()) {
    enum class Unit {
        DAY, HOUR, MINUTE, SECOND, MILLISECOND
    }

    fun format(duration: kotlin.time.Duration, smallestUnit: Unit = Unit.SECOND): String {
        var formattedStringComponents = mutableListOf<String>()
        var remainder = duration

        for (unit in Unit.values()) {
            val component = calculateComponent(unit, remainder)

            remainder = when (unit) {
                Unit.DAY -> remainder - component.days
                Unit.HOUR -> remainder - component.hours
                Unit.MINUTE -> remainder - component.minutes
                Unit.SECOND -> remainder - component.seconds
                Unit.MILLISECOND -> remainder - component.milliseconds
            }

            val unitDisplayName = unitDisplayName(unit)

            if (component > 0) {
                val formattedComponent = NumberFormat.getInstance(locale).format(component)
                formattedStringComponents.add("$formattedComponent$unitDisplayName")
            }

            if (unit == smallestUnit) {
                val formattedZero = NumberFormat.getInstance(locale).format(0)
                if (formattedStringComponents.isEmpty()) formattedStringComponents.add("$formattedZero$unitDisplayName")
                break
            }
        }

        return formattedStringComponents.joinToString(" ")
    }

    private fun calculateComponent(unit: Unit, remainder: Duration) = when (unit) {
        Unit.DAY -> remainder.inDays.toLong()
        Unit.HOUR -> remainder.inHours.toLong()
        Unit.MINUTE -> remainder.inMinutes.toLong()
        Unit.SECOND -> remainder.inSeconds.toLong()
        Unit.MILLISECOND -> remainder.inMilliseconds.toLong()
    }

    private fun unitDisplayName(unit: Unit) = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
        val measureFormat = MeasureFormat.getInstance(locale, MeasureFormat.FormatWidth.NARROW)
        when (unit) {
            DurationFormat.Unit.DAY -> measureFormat.getUnitDisplayName(MeasureUnit.DAY)
            DurationFormat.Unit.HOUR -> measureFormat.getUnitDisplayName(MeasureUnit.HOUR)
            DurationFormat.Unit.MINUTE -> measureFormat.getUnitDisplayName(MeasureUnit.MINUTE)
            DurationFormat.Unit.SECOND -> measureFormat.getUnitDisplayName(MeasureUnit.SECOND)
            DurationFormat.Unit.MILLISECOND -> measureFormat.getUnitDisplayName(MeasureUnit.MILLISECOND)
        }
    } else {
        when (unit) {
            Unit.DAY -> "day"
            Unit.HOUR -> "hour"
            Unit.MINUTE -> "min"
            Unit.SECOND -> "sec"
            Unit.MILLISECOND -> "msec"
        }
    }
}
0
Dharman 13 Agustus 2020, 15:16