【问题标题】:How to show localized date units in a formatted string in Java/Android?如何在 Java/Android 中的格式化字符串中显示本地化日期单位?
【发布时间】:2020-03-17 21:43:01
【问题描述】:

假设我有一段时间:

Period p = Period.ofDays(3);

我想用字符串中的“天”标签格式化句点,以得到这个字符串作为输出:

// "3 days"

...但是,我想本地化“days”组件,所以我不能使用如下格式的字符串,否则它只会在英文中正确显示:

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

Java/Kotlin/Android 中有哪些 API 可以表示某个时间段,例如某个语言环境中的几小时、几天、几周、几年?如果可以让 API 来做,我宁愿不自己本地化这些词。

【问题讨论】:

  • 例如,Time4J - here 是句点的法语语言环境文件。
  • 它不是Java内置的。

标签: java android date localization period


【解决方案1】:

正如一条评论中所建议的,您可以使用我的库 Time4J 并使用以下代码:

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

本教程还包含一个list of currently supported languages。顺便说一句,如果您对规范化或扩展 ISO 兼容性等其他功能感兴趣,java.time.Period 的 Time4J 等效项将是 net.time4j.Duration<CalendarUnit>

注意:如果您使用的是 Android,那么您应该使用姐妹库 Time4A 而不是 Time4J,但提供的代码将是相同的。

【讨论】:

    【解决方案2】:

    在使用带有本地化格式的 Kotlin Duration 类型时,因为找不到好的解决方案,我自己写了一个。它基于从 Android 9 开始提供的 API(用于本地化单位),但对于较低的 Android 版本回退到英文单位,因此它可以与目标较低的应用程序一起使用。

    这是它在使用方面的样子(请参阅 Kotlin Duration 类型以了解第一行):

    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"
    

    如您所见,您可以为DurationFormat 类型指定自定义locale。默认情况下,它使用Locale.getDefault()。还支持数字符号与罗马字母不同的语言(通过NumberFormat)。此外,您可以指定自定义smallestUnit,默认设置为SECOND,因此不会显示毫秒。请注意,任何值为0 的单位都将被忽略,如果整个数字为0,则将使用值为0 的最小单位。

    这是用于复制和粘贴的完整的DurationFormat 类型(也可用作GitHub gist,包括单元测试):

    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"
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2017-12-07
      • 2011-01-08
      • 2013-03-03
      • 2019-11-16
      • 2021-06-14
      • 1970-01-01
      • 1970-01-01
      • 2012-06-18
      • 2014-09-05
      相关资源
      最近更新 更多