mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-03 02:21:05 +08:00
[l10n] Add Infobar to adjust DevTools' locale
This CL adds an Infobar that allows the user to adjust DevTools'
locale. The Infobar is shown only under the following circumstances:
1) It hasn't been shown before
2) The localized DevTools experiment is enabled
3) DevTools is configured to use a language other than
'browserLanguage', which tries to match Chrome's locale
4) Chrome's UI language is also supported by DevTools and the
currently configured DevTools UI langauge differs from Chrome's.
Screenshot: https://imgur.com/a/GmazgOj
Bug: chromium:1163928
Change-Id: I83c956b534bdef5b5c7f555733d20fca613a1b33
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2944901
Commit-Queue: Simon Zünd <szuend@chromium.org>
Reviewed-by: Sigurd Schneider <sigurds@chromium.org>
This commit is contained in:
committed by
Devtools-frontend LUCI CQ
parent
5089cc55b3
commit
dd2f205a4c
@@ -29,8 +29,11 @@ export type DevToolsLocaleCreationOptions = {
|
||||
*/
|
||||
export class DevToolsLocale {
|
||||
readonly locale: string;
|
||||
readonly lookupClosestDevToolsLocale: (locale: string) => string;
|
||||
|
||||
private constructor(data: DevToolsLocaleData) {
|
||||
this.lookupClosestDevToolsLocale = data.lookupClosestDevToolsLocale;
|
||||
|
||||
// TODO(crbug.com/1163928): Use constant once setting actually exists.
|
||||
if (data.settingLanguage === 'browserLanguage') {
|
||||
this.locale = data.navigatorLanguage || 'en-US';
|
||||
@@ -38,7 +41,7 @@ export class DevToolsLocale {
|
||||
this.locale = data.settingLanguage;
|
||||
}
|
||||
|
||||
this.locale = data.lookupClosestDevToolsLocale(this.locale);
|
||||
this.locale = this.lookupClosestDevToolsLocale(this.locale);
|
||||
}
|
||||
|
||||
static instance(opts: DevToolsLocaleCreationOptions = {create: false}): DevToolsLocale {
|
||||
@@ -57,4 +60,26 @@ export class DevToolsLocale {
|
||||
// overwrite the locale.
|
||||
(this.locale as DevToolsLocale['locale']) = 'en-US';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true iff DevTools supports the language of the passed locale.
|
||||
* Note that it doesn't have to be a one-to-one match, e.g. if DevTools supports
|
||||
* 'de', then passing 'de-AT' will return true.
|
||||
*/
|
||||
languageIsSupportedByDevTools(localeString: string): boolean {
|
||||
return localeLanguagesMatch(localeString, this.lookupClosestDevToolsLocale(localeString));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true iff the two locales have matching languages. This means the
|
||||
* passing 'de-AT' and 'de-DE' will return true, while 'de-DE' and 'en' will
|
||||
* return false.
|
||||
*/
|
||||
export function localeLanguagesMatch(localeString1: string, localeString2: string): boolean {
|
||||
// @ts-ignore TODO(crbug.com/1163928) Wait for Intl support.
|
||||
const locale1 = new Intl.Locale(localeString1);
|
||||
// @ts-ignore TODO(crbug.com/1163928) Wait for Intl support.
|
||||
const locale2 = new Intl.Locale(localeString2);
|
||||
return locale1.language === locale2.language;
|
||||
}
|
||||
|
||||
@@ -11303,6 +11303,9 @@
|
||||
"ui/legacy/InspectorView.ts | closeDrawer": {
|
||||
"message": "Close drawer"
|
||||
},
|
||||
"ui/legacy/InspectorView.ts | devToolsLanguageMissmatch": {
|
||||
"message": "DevTools is now available in {PH1}!"
|
||||
},
|
||||
"ui/legacy/InspectorView.ts | moreTools": {
|
||||
"message": "More Tools"
|
||||
},
|
||||
@@ -11318,6 +11321,12 @@
|
||||
"ui/legacy/InspectorView.ts | reloadDevtools": {
|
||||
"message": "Reload DevTools"
|
||||
},
|
||||
"ui/legacy/InspectorView.ts | setToBrowserLanguage": {
|
||||
"message": "Always match Chrome's language"
|
||||
},
|
||||
"ui/legacy/InspectorView.ts | setToSpecificLanguage": {
|
||||
"message": "Switch DevTools to {PH1}"
|
||||
},
|
||||
"ui/legacy/ListWidget.ts | addString": {
|
||||
"message": "Add"
|
||||
},
|
||||
|
||||
@@ -11303,6 +11303,9 @@
|
||||
"ui/legacy/InspectorView.ts | closeDrawer": {
|
||||
"message": "Ĉĺôśê d́r̂áŵér̂"
|
||||
},
|
||||
"ui/legacy/InspectorView.ts | devToolsLanguageMissmatch": {
|
||||
"message": "D̂év̂T́ôól̂ś îś n̂óŵ áv̂áîĺâb́l̂é îń {PH1}!"
|
||||
},
|
||||
"ui/legacy/InspectorView.ts | moreTools": {
|
||||
"message": "M̂ór̂é T̂óôĺŝ"
|
||||
},
|
||||
@@ -11318,6 +11321,12 @@
|
||||
"ui/legacy/InspectorView.ts | reloadDevtools": {
|
||||
"message": "R̂él̂óâd́ D̂év̂T́ôól̂ś"
|
||||
},
|
||||
"ui/legacy/InspectorView.ts | setToBrowserLanguage": {
|
||||
"message": "Âĺŵáŷś m̂át̂ćĥ Ćĥŕôḿê'ś l̂án̂ǵûáĝé"
|
||||
},
|
||||
"ui/legacy/InspectorView.ts | setToSpecificLanguage": {
|
||||
"message": "Ŝẃît́ĉh́ D̂év̂T́ôól̂ś t̂ó {PH1}"
|
||||
},
|
||||
"ui/legacy/ListWidget.ts | addString": {
|
||||
"message": "Âd́d̂"
|
||||
},
|
||||
|
||||
@@ -79,6 +79,26 @@ const UIStrings = {
|
||||
*@description Text for context menu action to move a tab to the drawer
|
||||
*/
|
||||
moveToBottom: 'Move to bottom',
|
||||
/**
|
||||
* @description Text shown in a prompt to the user when DevTools is started and the
|
||||
* currently selected DevTools locale does not match Chrome's locale.
|
||||
* The placeholder is the current Chrome language.
|
||||
* @example {German} PH1
|
||||
*/
|
||||
devToolsLanguageMissmatch: 'DevTools is now available in {PH1}!',
|
||||
/**
|
||||
* @description An option the user can select when we notice that DevTools
|
||||
* is configured with a different locale than Chrome. This option means DevTools will
|
||||
* always try and display the DevTools UI in the same language as Chrome.
|
||||
*/
|
||||
setToBrowserLanguage: 'Always match Chrome\'s language',
|
||||
/**
|
||||
* @description An option the user can select when DevTools notices that DevTools
|
||||
* is configured with a different locale than Chrome. This option means DevTools UI
|
||||
* will be switched to the language specified in the placeholder.
|
||||
* @example {German} PH1
|
||||
*/
|
||||
setToSpecificLanguage: 'Switch DevTools to {PH1}',
|
||||
};
|
||||
const str_ = i18n.i18n.registerUIStrings('ui/legacy/InspectorView.ts', UIStrings);
|
||||
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
||||
@@ -157,6 +177,12 @@ export class InspectorView extends VBox implements ViewLocationResolver {
|
||||
const panelName = (event.data as string);
|
||||
this.showPanel(panelName);
|
||||
}
|
||||
|
||||
if (shouldShowLocaleInfobar()) {
|
||||
const infobar = createLocaleInfobar();
|
||||
infobar.setParentView(this);
|
||||
this._attachInfobar(infobar);
|
||||
}
|
||||
}
|
||||
|
||||
static instance(opts: {
|
||||
@@ -371,17 +397,12 @@ export class InspectorView extends VBox implements ViewLocationResolver {
|
||||
{
|
||||
text: i18nString(UIStrings.reloadDevtools),
|
||||
highlight: true,
|
||||
delegate: (): void => {
|
||||
if (DockController.instance().canDock() && DockController.instance().dockSide() === State.Undocked) {
|
||||
Host.InspectorFrontendHost.InspectorFrontendHostInstance.setIsDocked(true, function() {});
|
||||
}
|
||||
Host.InspectorFrontendHost.InspectorFrontendHostInstance.reattach(() => window.location.reload());
|
||||
},
|
||||
delegate: (): void => reloadDevTools(),
|
||||
dismiss: false,
|
||||
},
|
||||
]);
|
||||
infobar.setParentView(this);
|
||||
this._attachReloadRequiredInfobar(infobar);
|
||||
this._attachInfobar(infobar);
|
||||
this._reloadRequiredInfobar = infobar;
|
||||
infobar.setCloseCallback(() => {
|
||||
delete this._reloadRequiredInfobar;
|
||||
@@ -389,14 +410,85 @@ export class InspectorView extends VBox implements ViewLocationResolver {
|
||||
}
|
||||
}
|
||||
|
||||
_attachReloadRequiredInfobar(infobar: Infobar): void {
|
||||
_createInfoBarDiv(): void {
|
||||
if (!this._infoBarDiv) {
|
||||
this._infoBarDiv = (document.createElement('div') as HTMLDivElement);
|
||||
this._infoBarDiv.classList.add('flex-none');
|
||||
this.contentElement.insertBefore(this._infoBarDiv, this.contentElement.firstChild);
|
||||
}
|
||||
this._infoBarDiv.appendChild(infobar.element);
|
||||
}
|
||||
|
||||
_attachInfobar(infobar: Infobar): void {
|
||||
this._createInfoBarDiv();
|
||||
this._infoBarDiv?.appendChild(infobar.element);
|
||||
}
|
||||
}
|
||||
|
||||
function getDisableLocaleInfoBarSetting(): Common.Settings.Setting<boolean> {
|
||||
return Common.Settings.Settings.instance().createSetting('disableLocaleInfoBar', false);
|
||||
}
|
||||
|
||||
function shouldShowLocaleInfobar(): boolean {
|
||||
if (!Root.Runtime.experiments.isEnabled(Root.Runtime.ExperimentName.LOCALIZED_DEVTOOLS)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (getDisableLocaleInfoBarSetting().get()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const languageSettingValue = Common.Settings.Settings.instance().moduleSetting<string>('language').get();
|
||||
if (languageSettingValue === 'browserLanguage') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// When the selected DevTools locale differs from the locale of the browser UI, we want to notify
|
||||
// users only once, that they have the opportunity to adjust DevTools locale to match Chrome's locale.
|
||||
return !i18n.DevToolsLocale.localeLanguagesMatch(navigator.language, languageSettingValue) &&
|
||||
i18n.DevToolsLocale.DevToolsLocale.instance().languageIsSupportedByDevTools(navigator.language);
|
||||
}
|
||||
|
||||
function createLocaleInfobar(): Infobar {
|
||||
const devtoolsLocale = i18n.DevToolsLocale.DevToolsLocale.instance();
|
||||
const closestSupportedLocale = devtoolsLocale.lookupClosestDevToolsLocale(navigator.language);
|
||||
// @ts-ignore TODO(crbug.com/1163928) Wait for Intl support.
|
||||
const locale = new Intl.Locale(closestSupportedLocale);
|
||||
const closestSupportedLanguageInCurrentLocale =
|
||||
new Intl.DisplayNames([devtoolsLocale.locale], {type: 'language'}).of(locale.language);
|
||||
|
||||
const languageSetting = Common.Settings.Settings.instance().moduleSetting<string>('language');
|
||||
return new Infobar(
|
||||
InfobarType.Info, i18nString(UIStrings.devToolsLanguageMissmatch, {PH1: closestSupportedLanguageInCurrentLocale}),
|
||||
[
|
||||
{
|
||||
text: i18nString(UIStrings.setToBrowserLanguage),
|
||||
highlight: true,
|
||||
delegate: (): void => {
|
||||
languageSetting.set('browserLanguage');
|
||||
getDisableLocaleInfoBarSetting().set(true);
|
||||
reloadDevTools();
|
||||
},
|
||||
dismiss: true,
|
||||
},
|
||||
{
|
||||
text: i18nString(UIStrings.setToSpecificLanguage, {PH1: closestSupportedLanguageInCurrentLocale}),
|
||||
highlight: true,
|
||||
delegate: (): void => {
|
||||
languageSetting.set(closestSupportedLocale);
|
||||
getDisableLocaleInfoBarSetting().set(true);
|
||||
reloadDevTools();
|
||||
},
|
||||
dismiss: true,
|
||||
},
|
||||
],
|
||||
getDisableLocaleInfoBarSetting());
|
||||
}
|
||||
|
||||
function reloadDevTools(): void {
|
||||
if (DockController.instance().canDock() && DockController.instance().dockSide() === State.Undocked) {
|
||||
Host.InspectorFrontendHost.InspectorFrontendHostInstance.setIsDocked(true, function() {});
|
||||
}
|
||||
Host.InspectorFrontendHost.InspectorFrontendHostInstance.reattach(() => window.location.reload());
|
||||
}
|
||||
|
||||
let actionDelegateInstance: ActionDelegate;
|
||||
|
||||
@@ -79,4 +79,28 @@ describe('DevToolsLocale', () => {
|
||||
assert.strictEqual(devToolsLocale.locale, 'en-US');
|
||||
});
|
||||
});
|
||||
|
||||
describe('languageIsSupportedByDevTools', () => {
|
||||
it('returns true if the locale is supported, false otherwise', () => {
|
||||
const data: i18n.DevToolsLocale.DevToolsLocaleData = {
|
||||
settingLanguage: 'zh-HK',
|
||||
navigatorLanguage: '',
|
||||
lookupClosestDevToolsLocale: () => 'zh',
|
||||
};
|
||||
const devToolsLocale = i18n.DevToolsLocale.DevToolsLocale.instance({create: true, data});
|
||||
|
||||
assert.isTrue(devToolsLocale.languageIsSupportedByDevTools('zh-HK'));
|
||||
assert.isFalse(devToolsLocale.languageIsSupportedByDevTools('de-DE'));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('localeLanguagesMatch', () => {
|
||||
it('returns true if the language part of a locale matches, false otherwise', () => {
|
||||
assert.isTrue(i18n.DevToolsLocale.localeLanguagesMatch('de-DE', 'de-AT'));
|
||||
assert.isTrue(i18n.DevToolsLocale.localeLanguagesMatch('de-DE', 'de'));
|
||||
|
||||
assert.isFalse(i18n.DevToolsLocale.localeLanguagesMatch('de', 'en'));
|
||||
assert.isFalse(i18n.DevToolsLocale.localeLanguagesMatch('de-AT', 'en-US'));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -55,7 +55,7 @@ describe('serialize/deserialize round-trip', () => {
|
||||
|
||||
describe('getLocalizedLanguageRegion', () => {
|
||||
function createMockDevToolsLocale(locale: string): i18n.DevToolsLocale.DevToolsLocale {
|
||||
return {locale, forceFallbackLocale: () => {}};
|
||||
return {locale, forceFallbackLocale: () => {}} as i18n.DevToolsLocale.DevToolsLocale;
|
||||
}
|
||||
|
||||
it('build the correct language/region string', () => {
|
||||
|
||||
Reference in New Issue
Block a user