Skip to content

Commit db46166

Browse files
rokobchaselatta
authored andcommitted
[embedder][glfw] Add support for locales to glfw shell (flutter#22657)
The other linux shell (and all the other embedding) have support for getting the locales from the system and sending them over the flutter/localization channel. The glfw shell does not have that which is causing a crash on an assert now that Locale is no longer nullable in Platform. This adds a similar approach to what is going on over in the other linux shell.
1 parent d3173a1 commit db46166

File tree

8 files changed

+534
-0
lines changed

8 files changed

+534
-0
lines changed

BUILD.gn

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import("//flutter/common/config.gni")
66
import("//flutter/shell/platform/config.gni")
7+
import("//flutter/shell/platform/glfw/config.gni")
78
import("//flutter/testing/testing.gni")
89

910
# Whether to build the dartdevc sdk, libraries, and source files
@@ -140,6 +141,10 @@ group("flutter") {
140141
if (is_linux) {
141142
public_deps +=
142143
[ "//flutter/shell/platform/linux:flutter_linux_unittests" ]
144+
if (build_glfw_shell) {
145+
public_deps +=
146+
[ "//flutter/shell/platform/glfw:flutter_glfw_unittests" ]
147+
}
143148
}
144149

145150
if (is_mac) {

ci/licenses_golden/licenses_flutter

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1298,6 +1298,9 @@ FILE: ../../../flutter/shell/platform/glfw/keyboard_hook_handler.h
12981298
FILE: ../../../flutter/shell/platform/glfw/platform_handler.cc
12991299
FILE: ../../../flutter/shell/platform/glfw/platform_handler.h
13001300
FILE: ../../../flutter/shell/platform/glfw/public/flutter_glfw.h
1301+
FILE: ../../../flutter/shell/platform/glfw/system_utils.cc
1302+
FILE: ../../../flutter/shell/platform/glfw/system_utils.h
1303+
FILE: ../../../flutter/shell/platform/glfw/system_utils_test.cc
13011304
FILE: ../../../flutter/shell/platform/glfw/text_input_plugin.cc
13021305
FILE: ../../../flutter/shell/platform/glfw/text_input_plugin.h
13031306
FILE: ../../../flutter/shell/platform/linux/egl_utils.cc

shell/platform/glfw/BUILD.gn

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
# Use of this source code is governed by a BSD-style license that can be
33
# found in the LICENSE file.
44

5+
import("//flutter/testing/testing.gni")
6+
57
_public_headers = [ "public/flutter_glfw.h" ]
68

79
# Any files that are built by clients (client_wrapper code, library headers for
@@ -28,6 +30,8 @@ source_set("flutter_glfw_headers") {
2830
}
2931

3032
source_set("flutter_glfw") {
33+
public = [ "system_utils.h" ]
34+
3135
sources = [
3236
"event_loop.cc",
3337
"event_loop.h",
@@ -41,6 +45,7 @@ source_set("flutter_glfw") {
4145
"keyboard_hook_handler.h",
4246
"platform_handler.cc",
4347
"platform_handler.h",
48+
"system_utils.cc",
4449
"text_input_plugin.cc",
4550
"text_input_plugin.h",
4651
]
@@ -71,6 +76,21 @@ source_set("flutter_glfw") {
7176
}
7277
}
7378

79+
test_fixtures("flutter_glfw_fixtures") {
80+
fixtures = []
81+
}
82+
83+
executable("flutter_glfw_unittests") {
84+
testonly = true
85+
sources = [ "system_utils_test.cc" ]
86+
deps = [
87+
":flutter_glfw",
88+
":flutter_glfw_fixtures",
89+
"//flutter/shell/platform/embedder:embedder_headers",
90+
"//flutter/testing",
91+
]
92+
}
93+
7494
copy("publish_headers_glfw") {
7595
sources = _public_headers
7696
outputs = [ "$root_out_dir/{{source_file_part}}" ]

shell/platform/glfw/flutter_glfw.cc

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "flutter/shell/platform/glfw/key_event_handler.h"
2323
#include "flutter/shell/platform/glfw/keyboard_hook_handler.h"
2424
#include "flutter/shell/platform/glfw/platform_handler.h"
25+
#include "flutter/shell/platform/glfw/system_utils.h"
2526
#include "flutter/shell/platform/glfw/text_input_plugin.h"
2627

2728
// GLFW_TRUE & GLFW_FALSE are introduced since libglfw-3.3,
@@ -690,6 +691,27 @@ static bool RunFlutterEngine(
690691
return true;
691692
}
692693

694+
// Passes locale information to the Flutter engine.
695+
static void SetUpLocales(FlutterDesktopEngineState* state) {
696+
std::vector<flutter::LanguageInfo> languages =
697+
flutter::GetPreferredLanguageInfo();
698+
std::vector<FlutterLocale> flutter_locales =
699+
flutter::ConvertToFlutterLocale(languages);
700+
// Convert the locale list to the locale pointer list that must be provided.
701+
std::vector<const FlutterLocale*> flutter_locale_list;
702+
flutter_locale_list.reserve(flutter_locales.size());
703+
std::transform(
704+
flutter_locales.begin(), flutter_locales.end(),
705+
std::back_inserter(flutter_locale_list),
706+
[](const auto& arg) -> const auto* { return &arg; });
707+
FlutterEngineResult result = FlutterEngineUpdateLocales(
708+
state->flutter_engine, flutter_locale_list.data(),
709+
flutter_locale_list.size());
710+
if (result != kSuccess) {
711+
std::cerr << "Failed to set up Flutter locales." << std::endl;
712+
}
713+
}
714+
693715
// Populates |state|'s helper object fields that are common to normal and
694716
// headless mode.
695717
//
@@ -713,6 +735,8 @@ static void SetUpCommonEngineState(FlutterDesktopEngineState* state,
713735
// System channel handler.
714736
state->platform_handler = std::make_unique<flutter::PlatformHandler>(
715737
state->internal_plugin_registrar->messenger(), window);
738+
739+
SetUpLocales(state);
716740
}
717741

718742
bool FlutterDesktopInit() {

shell/platform/glfw/system_utils.cc

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#include "flutter/shell/platform/glfw/system_utils.h"
6+
7+
#include <cstdlib>
8+
#include <sstream>
9+
10+
namespace flutter {
11+
12+
namespace {
13+
14+
const char* GetLocaleStringFromEnvironment() {
15+
const char* retval;
16+
retval = getenv("LANGUAGE");
17+
if ((retval != NULL) && (retval[0] != '\0')) {
18+
return retval;
19+
}
20+
retval = getenv("LC_ALL");
21+
if ((retval != NULL) && (retval[0] != '\0')) {
22+
return retval;
23+
}
24+
retval = getenv("LC_MESSAGES");
25+
if ((retval != NULL) && (retval[0] != '\0')) {
26+
return retval;
27+
}
28+
retval = getenv("LANG");
29+
if ((retval != NULL) && (retval[0] != '\0')) {
30+
return retval;
31+
}
32+
33+
return NULL;
34+
}
35+
36+
// The least specific to most specific components of a locale.
37+
enum Component {
38+
kCodeset = 1 << 0,
39+
kTerritory = 1 << 1,
40+
kModifier = 1 << 2,
41+
};
42+
43+
// Construct a mask indicating which of the components in |info| are set.
44+
int ComputeVariantMask(const LanguageInfo& info) {
45+
int mask = 0;
46+
if (!info.territory.empty()) {
47+
mask |= kTerritory;
48+
}
49+
if (!info.codeset.empty()) {
50+
mask |= kCodeset;
51+
}
52+
if (!info.modifier.empty()) {
53+
mask |= kModifier;
54+
}
55+
return mask;
56+
}
57+
58+
// Appends most specific to least specific variants of |info| to |languages|.
59+
// For example, "de_DE@euro" would append "de_DE@euro", "de@euro", "de_DE",
60+
// and "de".
61+
void AppendLocaleVariants(std::vector<LanguageInfo>& languages,
62+
LanguageInfo info) {
63+
int mask = ComputeVariantMask(info);
64+
for (int i = mask; i >= 0; --i) {
65+
if ((i & ~mask) == 0) {
66+
LanguageInfo variant;
67+
variant.language = info.language;
68+
69+
if (i & kTerritory) {
70+
variant.territory = info.territory;
71+
}
72+
if (i & kCodeset) {
73+
variant.codeset = info.codeset;
74+
}
75+
if (i & kModifier) {
76+
variant.modifier = info.modifier;
77+
}
78+
languages.push_back(variant);
79+
}
80+
}
81+
}
82+
83+
// Parses a locale into its components.
84+
LanguageInfo ParseLocale(const std::string& locale) {
85+
// Locales are of the form "language[_territory][.codeset][@modifier]"
86+
LanguageInfo result;
87+
std::string::size_type end = locale.size();
88+
std::string::size_type modifier_pos = locale.rfind('@');
89+
if (modifier_pos != std::string::npos) {
90+
result.modifier = locale.substr(modifier_pos + 1, end - modifier_pos - 1);
91+
end = modifier_pos;
92+
}
93+
94+
std::string::size_type codeset_pos = locale.rfind('.', end);
95+
if (codeset_pos != std::string::npos) {
96+
result.codeset = locale.substr(codeset_pos + 1, end - codeset_pos - 1);
97+
end = codeset_pos;
98+
}
99+
100+
std::string::size_type territory_pos = locale.rfind('_', end);
101+
if (territory_pos != std::string::npos) {
102+
result.territory =
103+
locale.substr(territory_pos + 1, end - territory_pos - 1);
104+
end = territory_pos;
105+
}
106+
107+
result.language = locale.substr(0, end);
108+
109+
return result;
110+
}
111+
112+
} // namespace
113+
114+
std::vector<LanguageInfo> GetPreferredLanguageInfo() {
115+
const char* locale_string;
116+
locale_string = GetLocaleStringFromEnvironment();
117+
if (!locale_string || locale_string[0] == '\0') {
118+
// This is the default locale if none is specified according to ISO C.
119+
locale_string = "C";
120+
}
121+
std::istringstream locales_stream(locale_string);
122+
std::vector<LanguageInfo> languages;
123+
std::string s;
124+
while (getline(locales_stream, s, ':')) {
125+
LanguageInfo info = ParseLocale(s);
126+
AppendLocaleVariants(languages, info);
127+
}
128+
return languages;
129+
}
130+
131+
std::vector<FlutterLocale> ConvertToFlutterLocale(
132+
const std::vector<LanguageInfo>& languages) {
133+
std::vector<FlutterLocale> flutter_locales;
134+
flutter_locales.reserve(languages.size());
135+
for (const auto& info : languages) {
136+
FlutterLocale locale = {};
137+
locale.struct_size = sizeof(FlutterLocale);
138+
locale.language_code = info.language.c_str();
139+
if (!info.territory.empty()) {
140+
locale.country_code = info.territory.c_str();
141+
}
142+
if (!info.codeset.empty()) {
143+
locale.script_code = info.codeset.c_str();
144+
}
145+
if (!info.modifier.empty()) {
146+
locale.variant_code = info.modifier.c_str();
147+
}
148+
flutter_locales.push_back(locale);
149+
}
150+
151+
return flutter_locales;
152+
}
153+
154+
} // namespace flutter

shell/platform/glfw/system_utils.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#ifndef FLUTTER_SHELL_PLATFORM_GLFW_SYSTEM_UTILS_H_
6+
#define FLUTTER_SHELL_PLATFORM_GLFW_SYSTEM_UTILS_H_
7+
8+
#include <string>
9+
#include <vector>
10+
11+
#include "flutter/shell/platform/embedder/embedder.h"
12+
13+
namespace flutter {
14+
15+
// Components of a system language/locale.
16+
struct LanguageInfo {
17+
std::string language;
18+
std::string territory;
19+
std::string codeset;
20+
std::string modifier;
21+
};
22+
23+
// Returns the list of user-preferred languages, in preference order,
24+
// parsed into LanguageInfo structures.
25+
std::vector<LanguageInfo> GetPreferredLanguageInfo();
26+
27+
// Converts a vector of LanguageInfo structs to a vector of FlutterLocale
28+
// structs. |languages| must outlive the returned value, since the returned
29+
// elements have pointers into it.
30+
std::vector<FlutterLocale> ConvertToFlutterLocale(
31+
const std::vector<LanguageInfo>& languages);
32+
33+
} // namespace flutter
34+
35+
#endif // FLUTTER_SHELL_PLATFORM_GLFW_SYSTEM_UTILS_H_

0 commit comments

Comments
 (0)