法语的本地化持续时间格式

时间:2015-08-20 11:03:46

标签: java python datetime localization

Time4J's code example的Python模拟是什么:

// duration in seconds normalized to hours, minutes and seconds
Duration<?> dur = Duration.of(337540, ClockUnit.SECONDS).with(Duration.STD_CLOCK_PERIOD);

// custom duration format => hh:mm:ss
String s1 = Duration.Formatter.ofPattern("hh:mm:ss").format(dur);
System.out.println(s1); // output: 93:45:40

// localized duration format for french
String s2 = PrettyTime.of(Locale.FRANCE).print(dur, TextWidth.WIDE);
System.out.println(s2); // output: 93 heures, 45 minutes et 40 secondes

很容易获得93:45:40

#!/usr/bin/env python3
from datetime import timedelta

dur = timedelta(seconds=337540)
print(dur) # -> 3 days, 21:45:40
fields = {}
fields['hours'], seconds = divmod(dur // timedelta(seconds=1), 3600)
fields['minutes'], fields['seconds'] = divmod(seconds, 60)
print("%(hours)02d:%(minutes)02d:%(seconds)02d" % fields) # -> 93:45:40

但是如何在Python中模拟PrettyTime.of(Locale.FRANCE).print(dur, TextWidth.WIDE) Java代码(不对单元进行硬编码)?

4 个答案:

答案 0 :(得分:1)

babel module允许接近所需的输出:

from babel.dates import format_timedelta # $ pip install babel

print(", ".join(format_timedelta(timedelta(**{unit: fields[unit]}),
                                 granularity=unit.rstrip('s'),
                                 threshold=fields[unit] + 1,
                                 locale='fr')
                for unit in "hours minutes seconds".split()))
# -> 93 heures, 45 minutes, 40 secondes

它自动处理语言环境和复数形式,例如,它产生的dur = timedelta(seconds=1)

0 heure, 0 minute, 1 seconde

也许更好的解决方案是使用标准工具(例如gettext)手动翻译格式字符串。

答案 1 :(得分:0)

这个humanize包可能有所帮助。它有法语本地化,或者您可以添加自己的。对于python 2.7和3.3。

答案 2 :(得分:0)

使用pendulum module

>>> import pendulum
>>> it = pendulum.interval(seconds=337540)
>>> it.in_words(locale='fr_FR')
'3 jours 21 heures 45 minutes 40 secondes'

答案 3 :(得分:0)

如果您使用的是Kotlin,我刚遇到了类似的问题,即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"

如您所见,您可以为locale类型指定自定义DurationFormat。默认情况下,它使用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"
        }
    }
}