如何通过设备位置在Flutter中获取时区、语言和国家ID?

138

当我在我的 Android 或 iOS 设备上运行项目时,我希望能够获取时区、语言和国家 ID。它应该从设备的位置中检测到时区、语言和国家 ID。

我可以通过内部库获得这些吗?

10个回答

242

Flutter语言环境解释

首先,您应该了解系统设置和应用程序设置之间的区别:

  • 系统设置 - 系统为您的应用程序提供的用户偏好设置信息。
  • 应用程序设置 - 您明确或隐含地决定的应用程序语言环境。您可以允许用户选择应用程序的任何语言环境,也可以让Flutter为您完成。

获取当前系统偏好设置

获取当前默认系统语言环境

import 'dart:io';

final String defaultLocale = Platform.localeName; // Returns locale string in the form 'en_US'

获取系统区域设置列表

import 'package:flutter/material.dart';

final List<Locale> systemLocales = WidgetsBinding.instance.window.locales; // Returns the list of locales that user defined in the system settings.

或者

import 'dart:ui';

final List<Locale> systemLocales = window.locales;

应用程序的本地化

MaterialApp小部件支持本地化。它接受您决定支持的应用程序语言环境列表。这意味着您应该在小部件初始化中明确定义它们,并提供所谓的“本地化代理”,以将实际翻译为所选语言环境(localizationsDelegates属性)。如果您的应用程序除了en_US之外还支持其他任何语言环境,则必须提供代理。 当您请求应用程序语言环境时,您将从supportedLocales列表中获得一个确切的值

获取应用程序语言环境

final Locale appLocale = Localizations.localeOf(context);

只有在MaterialApp小部件已经初始化时才有效。您不能在MaterialApp初始化之前使用它。显然,如果您的应用程序小部件未初始化,则无法获取其语言环境。要使用它,您必须在子组件的build方法中调用此表达式。例如:

void main() async {
  runApp(
    MaterialApp(
      title: 'MyApp',
      home: MyApp(),
    )
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // Show the name of the current application locale
    return Text(Localizations.localeOf(context).toString());
  }
}


这将在屏幕上打印出en_US文本(因为这是默认语言环境,我们没有提供其他内容)。由于在此示例中我们没有明确定义应用程序的任何语言环境,因此系统语言环境设置无关紧要,并且返回默认语言环境。 捕捉系统语言环境更改 要能够对系统语言环境更改做出反应,您应该使用实现了WidgetsBindingObserver mixin的有状态小部件,并定义didChangeLocales方法,该方法将在系统语言环境更改时被调用:
  @override
  void didChangeLocales(List<Locale> locale) {
    // This is run when system locales are changed
    super.didChangeLocales(locale);
    setState(() {
      // Do actual stuff on the changes
    });
  }

完整示例

总结以上内容,以下是获取启动时区域设置并在系统设置更改时做出反应的可视化示例:

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // Get the initial locale values
  final String defaultSystemLocale = Platform.localeName;
  final List<Locale> systemLocales = WidgetsBinding.instance.window.locales;

  // Define locales that our app supports (no country codes, see comments below)
  final appSupportedLocales = <Locale>[
    Locale('ru'),
    Locale('en'),
  ];

  final MyApp myApp = MyApp(defaultSystemLocale, systemLocales);

  runApp(
    MaterialApp(
      title: 'MyApp',
      home: myApp,
      supportedLocales: appSupportedLocales,
      localizationsDelegates: [
        // These are default localization delegates that implement the very basic translations for standard controls and date/time formats.
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
      ],
    )
  );
}

class MyApp extends StatefulWidget {
  // Store initial locale settings here, they are unchanged
  final String initialDefaultSystemLocale;
  final List<Locale> initialSystemLocales;

  MyApp(this.initialDefaultSystemLocale, this.initialSystemLocales);

  @override
  _MyAppState createState() => _MyAppState();
}


class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
  // Store dynamic changeable locale settings here, they change with the system changes
  String currentDefaultSystemLocale;
  List<Locale> currentSystemLocales;

  // Here we read the current locale values
  void setCurrentValues() {
    currentSystemLocales = WidgetsBinding.instance.window.locales;
    currentDefaultSystemLocale = Platform.localeName;
  }

  @override
  void initState() {
    // This is run when the widget is first time initialized
    WidgetsBinding.instance.addObserver(this); // Subscribe to changes
    setCurrentValues();
    super.initState();
  }

  @override
  void didChangeLocales(List<Locale> locale) {
    // This is run when system locales are changed
    super.didChangeLocales(locale);
    // Update state with the new values and redraw controls
    setState(() {
      setCurrentValues();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        mainAxisAlignment: MainAxisAlignment.start,
        children: <Widget>[
          Text('Initial system default locale: ${widget.initialDefaultSystemLocale}.'),
          Text('Initial language code: ${widget.initialDefaultSystemLocale.split('_')[0]}, country code: ${widget.initialDefaultSystemLocale.split('_')[1]}.'),
          Text('Initial system locales:'),
          for (var locale in widget.initialSystemLocales) Text(locale.toString()),
          Text(''),
          Text('Current system default locale: ${currentDefaultSystemLocale}.'),
          Text('Current system locales:'),
          for (var locale in currentSystemLocales) Text(locale.toString()),
          Text(''),
          Text('Selected application locale: ${Localizations.localeOf(context).toString()}.'),
          Text(''),
          Text('Current date: ${Localizations.of<MaterialLocalizations>(context, MaterialLocalizations).formatFullDate(DateTime.now())}.'),
          Text('Current time zone: ${DateTime.now().timeZoneName} (offset ${DateTime.now().timeZoneOffset}).'),
        ],
      ),
    );
  }
}


支持的语言环境是没有国家代码定义的,这样可以展示当前应用语言环境选择的方式。在内部,MaterialApp小部件将应用支持的语言环境列表与系统传递的用户偏好列表进行比较,并选择最合适的语言环境。假设我们有以下系统语言环境列表(en_US,en_GB,ru_RU):** 点击缩略图查看完整截图。

initial list of locales

我们的应用程序启动时将看起来像这样:

initial app state

我们可以看到初始值和当前值明显相同。应用程序选择en作为其当前语言环境。
现在让我们将语言环境列表更改为以下内容,将en_GB语言环境移动到顶部,使其成为默认系统语言环境:

UK locale on top

该应用程序将反映这个变化:

app with the UK locale

该应用程序显然仍将en区域设置为当前区域设置,因为它是与en_USen_GB区域设置最接近的匹配项。
现在让我们将设置更改为俄语作为默认系统值:

RU locale on top

我们的应用将反映这一变化:

app with the RU locale

现在你可以看到,应用程序语言环境选择了 ru。
希望这有助于理解 Flutter 中的本地化工作原理,如何获取当前值以及如何反映系统更改。不解释区域设置委托的详细信息,因为这是不相关的话题。
附注:此代码仅在 Android 下测试。我认为只需进行轻微更改即可适应所有其他平台。

13
这就是如何回答一个问题。虽然只适用于安卓系统,但翻译得很出色。 - ChrisH
这是一个神级别的答案!我来寻找其他东西,但阅读这个让我没有后悔 :) @AmmyKang,你介意把它改成“正确”的答案吗? - Joel Broström
你的代码需要更新,包括super.didChangeLocales和WidgetsBinding.instance.addObserver(this)。 - zakiblacki
也,WidgetsBinding.instance.window已被弃用。 ): - Dylan Cross
现在应该使用WidgetsBinding.instance.platformDispatcher.locales来获取系统区域设置列表,因为window已被弃用。 - undefined

115

12
谢谢@Gunter,但我得到的国家代码是“US”,语言ID是“en”,我该如何根据我的当前位置获取正确的代码?由于我在印度,它应该显示“IND”。 - Ammy Kang
5
我的Locale是Localizations.localeOf(context)无法使用。myLocale.languageCode总是“en”。 - valerybodak
5
我做这件事时,会收到“失败的断言”和“未找到 Localizations 祖先的作用域不为空”的错误提示。 - John Smith Optional
5
Localizations.localeOf(context) 并不适用于所有情况:它返回的是 _当前上下文的语言环境_,该语言环境源自系统的语言设置,但已经被解析为 supportedLocales 中的某个语言环境。这是传递给 MaterialAppWidgetsApp 的参数,默认只有 [en_US]。使用 WidgetsBinding.instance.window.locale 可以获取原始的语言环境信息。 - Thomas
3
如果你们使用的是iOS系统,请不要忘记在info.plist中添加支持的语言,否则你将无法使用该功能。 - schankam
显示剩余8条评论

35

你可以使用

import 'dart:io' show Platform;

String languageCode = Platform.localeName.split('_')[0];
String countryCode = Platform.localeName.split('_')[1];

如果我将iOS模拟器更改为美国西班牙语,这对我无效。 我仍然得到“en”,“US”。 - Cliff Helsel
2
我之前修好了它。 - Cenk YAGMUR
这看起来比使用索引的解决方案更好。 - Martin Berger
2
Platform.localeName仅返回语言代码而没有国家代码(例如“en”)时,这将抛出RangeError。 - mrcendre

28

试一下:

import 'dart:ui' as ui;
......
 @override
  Widget build(BuildContext context) {
// get system language code
    _sysLng = ui.window.locale.languageCode;    
...

应用程序启动时始终返回 null。 - Oliver Dixon
5
无论什么情况下都会返回'en'。 - Oliver Dixon
使用iOS 13似乎存在一个bug,因为它返回null...所以我不认为这种方法是可靠的。从现在开始,我只使用Locale myLocale = Localizations.localeOf(context)。 - Louis de Decker
不是系统语言,而是窗口语言,但系统可以默认启用其他语言。例如,当使用自定义区域设置环境启动应用程序时。 - e-info128

23
如果您编写Platform.localeName,它将为您提供像"en_US"、"tr_TR"这样的语言和国家信息。 要仅获取"en"或"tr",您可以使用substring()并取前2个字母。
String deviceLanguage= Platform.localeName.substring(0,2);

它只会给你"en"或"tr"等...

如果你想要国家代码,请使用以下内容;

String deviceCountry= Platform.localeName.substring(3,5);

它仅提供给您“US”或“TR”等...


7
使其正常工作的步骤如下:
  1. Add the package as a dependency to your pubspec.yaml file

    dependencies:
       flutter:
       sdk: flutter
    flutter_localizations:
       sdk: flutter
    
  2. Import the flutter_localizations library and specify localizationsDelegates and supportedLocales for MaterialApp

    import 'package:flutter_localizations/flutter_localizations.dart';
    
    MaterialApp(
    localizationsDelegates: [
        // ... app-specific localization delegate[s] here
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
    ],
    
    supportedLocales: [
        const Locale('en', ''), // English, no country code
        const Locale('es', ''), // Spanish, no country code
        // ... other locales the app supports
     ],
    // ...
    )
    
  3. For iOS only: open the ios module and add the supported languages under localization

在这里输入图片描述

  1. 获取当前语言环境

    Locale myLocale = Localizations.localeOf(context);

如果需要,您可以访问Locale的countryCodelanguageCode字符串属性。 在这里输入图片描述


3

对于时区,其他答案会给出缩写如“CET”,“MSK”等以及GMT偏移量。

要以Europe/Paris格式获取设备的时区,请使用flutter_native_timezone包

final String currentTimeZone = await FlutterNativeTimezone.getLocalTimezone();

2
延伸Alexander Pravdin的回答

系统区域设置

import 'package:flutter/material.dart';
// or import 'package:flutter/widgets.dart';
final List<Locale> systemLocales =
    WidgetsBinding.instance.platformDispatcher.locales;
print(systemLocales);

例如,此将从 chrome://settings/languages 返回首选语言列表。

应用程序使用的区域设置

import 'package:flutter/material.dart';
// or import 'package:flutter/widgets.dart';
final Locale appLocale = Localizations.localeOf(context);
print(appLocale);

文档链接:

  1. PlatformDispatcher.locales
  2. Localizations.localeOf()

2
在项目中使用easy_localization插件时,当前的代码将极大地简化获取设备的系统语言。由于该插件具有localeName的扩展功能。
import 'package:easy_localization/easy_localization.dart';

Platform.localeName.toLocale().languageCode
Platform.localeName.toLocale().countryCode

获取时区,已经有一个来自@Günter Zöchbauer的很好的答案,其中包含了文档链接。

感谢您的编辑。能否从这个库中获取时区信息?如果可以的话,请包含进去,因为问题中有人提到了。 - Jeremy Caney
1
很遗憾,在这个图书馆里没有这样的东西。根据时区,许多答案都更高。我只是建议了一种最简单的方法,如果在项目中使用这个库,可以获取系统的语言和国家信息。 - Никита HiTNoX
这很有道理,也是有用的信息。你可以编辑你的答案并做个注释吗?将其他答案之一作为“指示牌”链接以回答时区部分问题可能也会有用。 - Jeremy Caney

0
我的建议是:
import 'dart:io';
        
String getDeviceLanguage(){
  return Platform.localeName.contains("_")
      ? Platform.localeName.split("_")[0]
      : Platform.localeName;
}

这个页面上有一些解决方案,比如以下代码,但是这些代码对于(Filipino Pilipino - fil)和(Finnish - fi)不起作用,并且会得到相同的结果。
String deviceLanguage= Platform.localeName.substring(0,2);

根据Flutter的文档,通常结果包括一个语言(例如"en"),或者一个语言和国家代码(例如"en_US","de_AT"),或者一个语言、国家代码和字符集(例如"en_US.UTF-8")。
来源:https://api.flutter.dev/flutter/dart-io/Platform/localeName.html 来源:https://api.flutter.dev/flutter/flutter_localizations/GlobalMaterialLocalizations-class.html

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接