mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Pass full locale list with script and variant codes to framework (#6557)
* Locale Passing * Pass full locale list and script and variant codes to framework * Working Android locale list passing and fallback
This commit is contained in:
parent
04c860fa1d
commit
35340ceaf0
@ -42,14 +42,20 @@ void _updateWindowMetrics(double devicePixelRatio,
|
||||
|
||||
typedef _LocaleClosure = String Function();
|
||||
|
||||
String _localeClosure() => window._locale.toString();
|
||||
String _localeClosure() => window.locale.toString();
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
_LocaleClosure _getLocaleClosure() => _localeClosure;
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
void _updateLocale(String languageCode, String countryCode, String scriptCode, String variantCode) {
|
||||
window._locale = new Locale(languageCode, countryCode);
|
||||
void _updateLocales(List<String> locales) {
|
||||
const int stringsPerLocale = 4;
|
||||
final int numLocales = locales.length ~/ stringsPerLocale;
|
||||
window._locales = new List<Locale>(numLocales);
|
||||
for (int localeIndex = 0; localeIndex < numLocales; localeIndex++) {
|
||||
window._locales[localeIndex] = new Locale(locales[localeIndex * stringsPerLocale],
|
||||
locales[localeIndex * stringsPerLocale + 1]);
|
||||
}
|
||||
_invoke(window.onLocaleChanged, window._onLocaleChangedZone);
|
||||
}
|
||||
|
||||
|
||||
@ -152,6 +152,9 @@ class Locale {
|
||||
/// the region subtag should be uppercase.
|
||||
const Locale(this._languageCode, [ this._countryCode ]) : assert(_languageCode != null);
|
||||
|
||||
/// Empty locale constant. This is an invalid locale.
|
||||
static const Locale none = const Locale('', '');
|
||||
|
||||
/// The primary language subtag for the locale.
|
||||
///
|
||||
/// This must not be null.
|
||||
@ -426,19 +429,39 @@ class Window {
|
||||
_onMetricsChangedZone = Zone.current;
|
||||
}
|
||||
|
||||
/// The system-reported locale.
|
||||
/// The system-reported default locale of the device.
|
||||
///
|
||||
/// This establishes the language and formatting conventions that application
|
||||
/// should, if possible, use to render their user interface.
|
||||
///
|
||||
/// This is the first locale selected by the user and is the user's
|
||||
/// primary locale (the locale the device UI is displayed in)
|
||||
///
|
||||
/// This is equivalent to `locales.first` and will provide an empty non-null locale
|
||||
/// if the [locales] list has not been set or is empty.
|
||||
Locale get locale {
|
||||
if (_locales != null && _locales.isNotEmpty) {
|
||||
return _locales.first;
|
||||
}
|
||||
return Locale.none;
|
||||
}
|
||||
|
||||
/// The full system-reported supported locales of the device.
|
||||
///
|
||||
/// This establishes the language and formatting conventions that application
|
||||
/// should, if possible, use to render their user interface.
|
||||
///
|
||||
/// The list is ordered in order of priority, with lower-indexed locales being
|
||||
/// preferred over higher-indexed ones. The first element is the primary [locale].
|
||||
///
|
||||
/// The [onLocaleChanged] callback is called whenever this value changes.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [WidgetsBindingObserver], for a mechanism at the widgets layer to
|
||||
/// observe when this value changes.
|
||||
Locale get locale => _locale;
|
||||
Locale _locale;
|
||||
List<Locale> get locales => _locales;
|
||||
List<Locale> _locales;
|
||||
|
||||
/// A callback that is invoked whenever [locale] changes value.
|
||||
///
|
||||
|
||||
@ -162,21 +162,14 @@ void Window::UpdateWindowMetrics(const ViewportMetrics& metrics) {
|
||||
});
|
||||
}
|
||||
|
||||
void Window::UpdateLocale(const std::string& language_code,
|
||||
const std::string& country_code,
|
||||
const std::string& script_code,
|
||||
const std::string& variant_code) {
|
||||
void Window::UpdateLocales(const std::vector<std::string>& locales) {
|
||||
std::shared_ptr<tonic::DartState> dart_state = library_.dart_state().lock();
|
||||
if (!dart_state)
|
||||
return;
|
||||
tonic::DartState::Scope scope(dart_state);
|
||||
|
||||
DartInvokeField(library_.value(), "_updateLocale",
|
||||
DartInvokeField(library_.value(), "_updateLocales",
|
||||
{
|
||||
StdStringToDart(language_code),
|
||||
StdStringToDart(country_code),
|
||||
StdStringToDart(script_code),
|
||||
StdStringToDart(variant_code),
|
||||
tonic::ToDart<std::vector<std::string>>(locales),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -5,7 +5,9 @@
|
||||
#ifndef FLUTTER_LIB_UI_WINDOW_WINDOW_H_
|
||||
#define FLUTTER_LIB_UI_WINDOW_WINDOW_H_
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "flutter/fml/time/time_point.h"
|
||||
#include "flutter/lib/ui/semantics/semantics_update.h"
|
||||
@ -59,10 +61,7 @@ class Window final {
|
||||
|
||||
void DidCreateIsolate();
|
||||
void UpdateWindowMetrics(const ViewportMetrics& metrics);
|
||||
void UpdateLocale(const std::string& language_code,
|
||||
const std::string& country_code,
|
||||
const std::string& script_code,
|
||||
const std::string& variant_code);
|
||||
void UpdateLocales(const std::vector<std::string>& locales);
|
||||
void UpdateUserSettingsData(const std::string& data);
|
||||
void UpdateSemanticsEnabled(bool enabled);
|
||||
void UpdateAccessibilityFeatures(int32_t flags);
|
||||
|
||||
@ -124,8 +124,7 @@ std::unique_ptr<RuntimeController> RuntimeController::Clone() const {
|
||||
|
||||
bool RuntimeController::FlushRuntimeStateToIsolate() {
|
||||
return SetViewportMetrics(window_data_.viewport_metrics) &&
|
||||
SetLocale(window_data_.language_code, window_data_.country_code,
|
||||
window_data_.script_code, window_data_.variant_code) &&
|
||||
SetLocales(window_data_.locale_data) &&
|
||||
SetSemanticsEnabled(window_data_.semantics_enabled) &&
|
||||
SetAccessibilityFeatures(window_data_.accessibility_feature_flags_);
|
||||
}
|
||||
@ -140,22 +139,15 @@ bool RuntimeController::SetViewportMetrics(const ViewportMetrics& metrics) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RuntimeController::SetLocale(const std::string& language_code,
|
||||
const std::string& country_code,
|
||||
const std::string& script_code,
|
||||
const std::string& variant_code) {
|
||||
window_data_.language_code = language_code;
|
||||
window_data_.country_code = country_code;
|
||||
window_data_.script_code = script_code;
|
||||
window_data_.variant_code = variant_code;
|
||||
bool RuntimeController::SetLocales(
|
||||
const std::vector<std::string>& locale_data) {
|
||||
window_data_.locale_data = locale_data;
|
||||
|
||||
if (auto window = GetWindowIfAvailable()) {
|
||||
window->UpdateLocale(window_data_.language_code, window_data_.country_code,
|
||||
window_data_.script_code, window_data_.variant_code);
|
||||
window->UpdateLocales(locale_data);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RuntimeController::SetUserSettingsData(const std::string& data) {
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
#define FLUTTER_RUNTIME_RUNTIME_CONTROLLER_H_
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "flutter/common/task_runners.h"
|
||||
#include "flutter/flow/layers/layer_tree.h"
|
||||
@ -15,6 +16,8 @@
|
||||
#include "flutter/lib/ui/window/pointer_data_packet.h"
|
||||
#include "flutter/lib/ui/window/window.h"
|
||||
#include "flutter/runtime/dart_vm.h"
|
||||
#include "rapidjson/document.h"
|
||||
#include "rapidjson/stringbuffer.h"
|
||||
|
||||
namespace blink {
|
||||
class Scene;
|
||||
@ -40,10 +43,7 @@ class RuntimeController final : public WindowClient {
|
||||
|
||||
bool SetViewportMetrics(const ViewportMetrics& metrics);
|
||||
|
||||
bool SetLocale(const std::string& language_code,
|
||||
const std::string& country_code,
|
||||
const std::string& script_code,
|
||||
const std::string& variant_code);
|
||||
bool SetLocales(const std::vector<std::string>& locale_data);
|
||||
|
||||
bool SetUserSettingsData(const std::string& data);
|
||||
|
||||
@ -78,12 +78,29 @@ class RuntimeController final : public WindowClient {
|
||||
std::pair<bool, uint32_t> GetRootIsolateReturnCode();
|
||||
|
||||
private:
|
||||
struct Locale {
|
||||
Locale(std::string language_code_,
|
||||
std::string country_code_,
|
||||
std::string script_code_,
|
||||
std::string variant_code_)
|
||||
: language_code(language_code_),
|
||||
country_code(country_code_),
|
||||
script_code(script_code_),
|
||||
variant_code(variant_code_) {}
|
||||
|
||||
std::string language_code;
|
||||
std::string country_code;
|
||||
std::string script_code;
|
||||
std::string variant_code;
|
||||
};
|
||||
|
||||
struct WindowData {
|
||||
ViewportMetrics viewport_metrics;
|
||||
std::string language_code;
|
||||
std::string country_code;
|
||||
std::string script_code;
|
||||
std::string variant_code;
|
||||
std::vector<std::string> locale_data;
|
||||
std::string user_settings_data = "{}";
|
||||
bool semantics_enabled = false;
|
||||
bool assistive_technology_enabled = false;
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "flutter/common/settings.h"
|
||||
#include "flutter/fml/eintr_wrapper.h"
|
||||
@ -325,17 +326,22 @@ bool Engine::HandleLocalizationPlatformMessage(
|
||||
if (args == root.MemberEnd() || !args->value.IsArray())
|
||||
return false;
|
||||
|
||||
const auto& language = args->value[0];
|
||||
const auto& country = args->value[1];
|
||||
const auto& script = args->value[2];
|
||||
const auto& variant = args->value[3];
|
||||
|
||||
if (!language.IsString() || !country.IsString())
|
||||
const size_t strings_per_locale = 4;
|
||||
if (args->value.Size() % strings_per_locale != 0)
|
||||
return false;
|
||||
std::vector<std::string> locale_data;
|
||||
for (size_t locale_index = 0; locale_index < args->value.Size();
|
||||
locale_index += strings_per_locale) {
|
||||
if (!args->value[locale_index].IsString() ||
|
||||
!args->value[locale_index + 1].IsString())
|
||||
return false;
|
||||
locale_data.push_back(args->value[locale_index].GetString());
|
||||
locale_data.push_back(args->value[locale_index + 1].GetString());
|
||||
locale_data.push_back(args->value[locale_index + 2].GetString());
|
||||
locale_data.push_back(args->value[locale_index + 3].GetString());
|
||||
}
|
||||
|
||||
return runtime_controller_->SetLocale(language.GetString(),
|
||||
country.GetString(), script.GetString(),
|
||||
variant.GetString());
|
||||
return runtime_controller_->SetLocales(locale_data);
|
||||
}
|
||||
|
||||
void Engine::HandleSettingsPlatformMessage(blink::PlatformMessage* message) {
|
||||
|
||||
@ -37,6 +37,7 @@ import io.flutter.plugin.platform.PlatformPlugin;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URI;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
@ -170,7 +171,8 @@ public class FlutterView extends SurfaceView
|
||||
mImm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
mTextInputPlugin = new TextInputPlugin(this);
|
||||
|
||||
setLocale(getResources().getConfiguration().locale);
|
||||
|
||||
setLocales(getResources().getConfiguration());
|
||||
setUserSettings();
|
||||
}
|
||||
|
||||
@ -316,14 +318,43 @@ public class FlutterView extends SurfaceView
|
||||
mFlutterSettingsChannel.send(message);
|
||||
}
|
||||
|
||||
private void setLocale(Locale locale) {
|
||||
private void setLocales(Configuration config) {
|
||||
if (Build.VERSION.SDK_INT >= 24) {
|
||||
try {
|
||||
// Passes the full list of locales for android API >= 24 with reflection.
|
||||
Object localeList = config.getClass().getDeclaredMethod("getLocales").invoke(config);
|
||||
Method localeListGet = localeList.getClass().getDeclaredMethod("get", int.class);
|
||||
Method localeListSize = localeList.getClass().getDeclaredMethod("size");
|
||||
int localeCount = (int)localeListSize.invoke(localeList);
|
||||
List<String> data = new ArrayList<String>();
|
||||
for (int index = 0; index < localeCount; ++index) {
|
||||
Locale locale = (Locale)localeListGet.invoke(localeList, index);
|
||||
data.add(locale.getLanguage());
|
||||
data.add(locale.getCountry());
|
||||
data.add(locale.getScript());
|
||||
data.add(locale.getVariant());
|
||||
}
|
||||
mFlutterLocalizationChannel.invokeMethod("setLocale", data);
|
||||
return;
|
||||
} catch (Exception exception) {
|
||||
// Any exception is a failure. Resort to fallback of sending only one locale.
|
||||
}
|
||||
}
|
||||
// Fallback single locale passing for android API < 24. Should work always.
|
||||
Locale locale = config.locale;
|
||||
List<String> data = new ArrayList<String>();
|
||||
data.add(locale.getLanguage());
|
||||
data.add(locale.getCountry());
|
||||
data.add(locale.getScript());
|
||||
data.add(locale.getVariant());
|
||||
mFlutterLocalizationChannel.invokeMethod("setLocale", Arrays.asList(locale.getLanguage(), locale.getCountry(), locale.getScript(), locale.getVariant()));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
setLocale(newConfig.locale);
|
||||
setLocales(newConfig);
|
||||
setUserSettings();
|
||||
}
|
||||
|
||||
|
||||
@ -905,19 +905,26 @@ static blink::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* touch) {
|
||||
#pragma mark - Locale updates
|
||||
|
||||
- (void)onLocaleUpdated:(NSNotification*)notification {
|
||||
NSLocale* currentLocale = [NSLocale currentLocale];
|
||||
NSString* languageCode = [currentLocale objectForKey:NSLocaleLanguageCode];
|
||||
NSString* countryCode = [currentLocale objectForKey:NSLocaleCountryCode];
|
||||
NSString* scriptCode = [currentLocale objectForKey:NSLocaleScriptCode];
|
||||
NSString* variantCode = [currentLocale objectForKey:NSLocaleVariantCode];
|
||||
if (languageCode && countryCode)
|
||||
// We pass empty strings for undefined scripts and variants to ensure the JSON encoder/decoder
|
||||
// functions properly.
|
||||
[_localizationChannel.get() invokeMethod:@"setLocale"
|
||||
arguments:@[
|
||||
languageCode, countryCode, scriptCode ? scriptCode : @"",
|
||||
variantCode ? variantCode : @""
|
||||
]];
|
||||
NSArray<NSString*>* preferredLocales = [NSLocale preferredLanguages];
|
||||
NSMutableArray<NSString*>* data = [NSMutableArray new];
|
||||
for (NSString* localeID in preferredLocales) {
|
||||
NSLocale* currentLocale = [[NSLocale alloc] initWithLocaleIdentifier:localeID];
|
||||
NSString* languageCode = [currentLocale objectForKey:NSLocaleLanguageCode];
|
||||
NSString* countryCode = [currentLocale objectForKey:NSLocaleCountryCode];
|
||||
NSString* scriptCode = [currentLocale objectForKey:NSLocaleScriptCode];
|
||||
NSString* variantCode = [currentLocale objectForKey:NSLocaleVariantCode];
|
||||
if (!languageCode || !countryCode) {
|
||||
continue;
|
||||
}
|
||||
[data addObject:languageCode];
|
||||
[data addObject:countryCode];
|
||||
[data addObject:(scriptCode ? scriptCode : @"")];
|
||||
[data addObject:(variantCode ? variantCode : @"")];
|
||||
}
|
||||
if (data.count == 0) {
|
||||
return;
|
||||
}
|
||||
[_localizationChannel.get() invokeMethod:@"setLocale" arguments:data];
|
||||
}
|
||||
|
||||
#pragma mark - Set user settings
|
||||
|
||||
@ -100,7 +100,7 @@ void main() {
|
||||
};
|
||||
});
|
||||
|
||||
_updateLocale('en', 'US', '', '');
|
||||
_updateLocales(<String>['en', 'US', '', '']);
|
||||
expect(runZone, isNotNull);
|
||||
expect(runZone, same(innerZone));
|
||||
expect(locale, equals(const Locale('en', 'US')));
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user