You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何SwiftUI Widget中设置环境Locale(ja)仅部分代码生效?

Widget本地化设置不生效问题描述

我尝试给Widget设置本地化,添加了.environment(\.locale, Locale(identifier: "ja"))后,大部分代码未生效:

仅生效的代码

  • Text(entry.date, style: .date)

不生效的代码

  • Text(Date.now.advanced(by: 60*60*12), style: .relative)
  • if {} else {} 条件语句

预期显示效果

こんにちは世界!
2022年12月31日
11時間, 57分

实际显示效果

实际显示效果

相关代码

struct SuperWidgetEntryView : View {
    var entry: Provider.Entry

    var body: some View {
        WidgetView()
            .environment(\.locale, Locale(identifier: "ja"))
    }
    
    var languageCode: String? { Locale.autoupdatingCurrent.language.languageCode?.identifier }
    var localeCode: String? { Locale.autoupdatingCurrent.identifier }
    var isJapaneseLanguage: Bool { languageCode == "ja" }
    var isJapaneseLocale: Bool { localeCode == "ja_JP" }
        
    @ViewBuilder
    func WidgetView() -> some View {
        VStack {
            if isJapaneseLocale || isJapaneseLanguage {
                Text("こんにちは世界!")
            } else {
                Text("Hello, world!")
            }
            VStack {
                Text(entry.date, style: .date)
                Text(Date.now.advanced(by: 60*60*12), style: .relative)
            }
        }
    }
}

问题原因与解决方法

1. 条件判断不生效的核心原因

当前的isJapaneseLocale、isJapaneseLanguage是基于Locale.autoupdatingCurrent(系统全局区域设置)判断的,和你通过.environment给View设置的Locale完全无关。就算给View环境指定了日语Locale,这些计算属性依然会读取系统的区域设置,导致条件判断不随环境Locale变化。

2. 相对时间格式不生效的原因

Text(Date, style: .relative)在Widget中的表现和普通App不同,它默认依赖系统区域设置,而非View环境里的Locale。另外,直接在View中使用Date.now不符合Widget的更新机制,应该依赖entry.date来保证时间同步。

具体修复步骤

步骤1:改用环境Locale做条件判断

添加@Environment(\.locale)读取View环境中的Locale,替换原有的系统Locale判断:

@Environment(\.locale) private var locale

var isJapaneseLanguage: Bool { locale.language.languageCode?.identifier == "ja" }
var isJapaneseLocale: Bool { locale.identifier == "ja_JP" }

步骤2:手动处理相对时间的本地化

用RelativeDateTimeFormatter自定义相对时间格式,指定使用环境中的Locale:

private func relativeDateString(for date: Date) -> String {
    let formatter = RelativeDateTimeFormatter()
    formatter.locale = locale
    formatter.unitsStyle = .full
    return formatter.localizedString(for: date, relativeTo: entry.date)
}

同时把Date.now替换为entry.date.addingTimeInterval,保证时间和Widget Entry同步:

Text(relativeDateString(for: entry.date.addingTimeInterval(60*60*12)))

修复后的完整代码

struct SuperWidgetEntryView : View {
    var entry: Provider.Entry
    @Environment(\.locale) private var locale

    var body: some View {
        WidgetView()
            .environment(\.locale, Locale(identifier: "ja"))
    }
    
    var isJapaneseLanguage: Bool { locale.language.languageCode?.identifier == "ja" }
    var isJapaneseLocale: Bool { locale.identifier == "ja_JP" }
    
    private func relativeDateString(for date: Date) -> String {
        let formatter = RelativeDateTimeFormatter()
        formatter.locale = locale
        formatter.unitsStyle = .full
        return formatter.localizedString(for: date, relativeTo: entry.date)
    }
        
    @ViewBuilder
    func WidgetView() -> some View {
        VStack {
            if isJapaneseLocale || isJapaneseLanguage {
                Text("こんにちは世界!")
            } else {
                Text("Hello, world!")
            }
            VStack {
                Text(entry.date, style: .date)
                Text(relativeDateString(for: entry.date.addingTimeInterval(60*60*12)))
            }
        }
    }
}

额外说明

Widget的环境Locale存在一定限制,部分系统级格式化逻辑不会完全遵循View环境的Locale,因此需要手动通过Formatter控制本地化输出;同时所有依赖Locale的判断逻辑,必须基于View环境中的值,而非系统全局Locale。

内容的提问来源于stack exchange,提问作者user14119170

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 07:17:12