From eb99c4a0b2da4c5b0d317e18e1d1a17b98e8d403 Mon Sep 17 00:00:00 2001 From: "Jackie(Guokai) Han" Date: Tue, 18 Jun 2024 17:14:07 +0800 Subject: [PATCH 01/10] Proposal: Per-extension language preferences This first version of this proposal. --- .../per-extension-language-preferences.md | 234 ++++++++++++++++++ 1 file changed, 234 insertions(+) create mode 100644 proposals/per-extension-language-preferences.md diff --git a/proposals/per-extension-language-preferences.md b/proposals/per-extension-language-preferences.md new file mode 100644 index 00000000..9399c594 --- /dev/null +++ b/proposals/per-extension-language-preferences.md @@ -0,0 +1,234 @@ +# Proposal: Per-extension language preferences + +**Summary** + +This proposal allows developers (users) to set a specific language for their extension (which may be different from the browser or system default language) and create a language selection menu for users. + +**Document Metadata** + +**Author:** hanguokai + +**Sponsoring Browser:** Chromium + +**Contributors:** + +**Created:** 2024-06-16 + +**Related Issues:** [#258](https://github.com/w3c/webextensions/issues/258) + +## Motivation + +### Objective + +Prior to this proposal, browsers automatically selected a `browser.i18n` language for extensions, and developers (users) could not set a specific language. + +For multilingual users, sometimes they want to set a specific application (extension) to another language that is different from the system (browser). This need is not uncommon. +[Android 13](https://developer.android.com/guide/topics/resources/app-languages), [iOS 13 and macOS Catalina](https://developer.apple.com/news/?id=u2cfuj88) support a feature +called "Per-app language preferences (settings)". This proposal brings the same function to browser extensions. + +#### Use Cases + +- Extensions create a language selection menu for users. +- [Optional] Browsers could potentially provide a built-in language selection menu for users, just like Android and iOS. +- [Optional] Extensions could integrate third-party i18n implementations (frameworks) with the browser's built-in language selection menu. + +### Known Consumers + +This is a generic i18n feature, which means that all types of extensions can use it. Some extensions already provide this functionality via workarounds or non-browser.i18n implementations. + +## Specification + +### Definitions + +In this document: +- **locale** or **language code** or **language tag** is a string that represent a language, defined in [BCP 47](https://www.rfc-editor.org/info/bcp47). +For example, `en-US`, `zh-CN`, `fr`. It is used by `browser.i18n`, `Date`, `Intl` and various other APIs. +- **the user preferred language** is the language set by `i18n.setCurrentLanguage(code)`. The user may not have set it. +- **the extension displayed language** is the language which the extension is displayed in. Note that the extension displayed language may be different from the browser UI language. For example, if the user preferred language is not set, the browser UI language is English, and the extension only supports French (default) and Japanese, then the extension displayed language is French because `i18n.getMessage()` returns French message at this situation. + +### Schema + +```ts +/** + * Get the extension displayed language. + * return the language tag, e.g. en-US. + */ +i18n.getCurrentLanguage() : string + +/** + * Set the user preferred language. + * if code is null, revert to the unset state. + * if code is not valid, return a rejected Promise. + * else return a Promise resolved when the operation is complete. + */ +i18n.setCurrentLanguage(code: string) : Promise + +/** + * Get all languages that supported by this extension. + * return a Promise resolved with an array of language tags. + */ +i18n.getAllLanguages(): Promise + +/** + * After changing to a new language, the browser triggers a language changed event. + * The callback is function(code: string) : void. + * The code parameter in the callback is the new language. + */ +i18n.onLanguageChanged.addListener(callback) +``` + +### Behavior + +#### Behavior of `i18n.getCurrentLanguage()` + +This method return the language that the extension is displayed in. + +- If the extension doesn't use `browser.i18n` (there is no "_locales" directory), return `undefined`. +- If the preferred language is not set by `i18n.setCurrentLanguage()`, returns the current language used by `i18n.getMessage()`, assuming that all languages support all possible keys. +- If the preferred language is set by `i18n.setCurrentLanguage()`, and the extension supports this language, then return this language. +- If the preferred language is set by `i18n.setCurrentLanguage()`, but the extension doesn't support this language (no message file for this language), then treat as if the preferred language is not set. This is an edge case, for example, the language was removed when the extension was upgraded. + +##### Use Case 1: use the extension displayed language with other locale-related APIs. + +For example, if the preferred language is not set, the language of the browser UI is English (en-US), the extension supports French (default) and Japanese, then `i18n.getCurrentLanguage()` return `"fr"` because `i18n.getMessage()` return French message at this situation. + +```js +// use current extension displayed language to format date +const locale = browser.i18n.getCurrentLanguage(); +const today_date = new Date().toLocaleString(locale); // format date string in 'fr' + +// if don't use current extension displayed language, it may be formatted in other locale +const today_date = new Date().toLocaleString(); // format date string in 'en-US' +``` + +##### Use Case 2: show the current language in the extension language selection menu. + +#### Behavior of `i18n.setCurrentLanguage(code)` + +This method sets the user preferred language for this extension. + +- If the extension doesn't use `browser.i18n` (there is no "_locales" directory), return a rejected Promise. +- If the code is an invalid language tag or an unsupported language by this extension, return a rejected Promise. +- If the code is null, revert to the unset state. +- else do the following: + 1. save the language persistently, and make sure that the language is prioritized for use next time. + 1. return a resolved Promise. + 1. trigger `i18n.onLanguageChanged` event. + +In addition, when `i18n.setCurrentLanguage(code)` success: +- the browser should update related browser UI and extension UI, because some values in manifest.json may be changed, like `name`, `short_name`, `description` and `action.default_title`. +- Text from `i18n.getMessage()` and css files don't update. These are handled by developers. + +##### Persistence of `i18n.setCurrentLanguage(code)` + +This setting is persistent when: +- restart or upgrade the browser. +- disable then re-enable the extension. +- upgrade the extension to a new version. + +#### Behavior of `i18n.getAllLanguages()` + +This method returns an array of language tags that the extension supported, e.g. `["en", "fr", "ja", "zh-CN"]`. +This is a convenient method. Without this method, developers need to hardcode it into their code. + +#### Behavior of `i18n.onLanguageChanged` event + +When the user preferred language is changed to a new different language by `i18n.setCurrentLanguage(code)`, this event is fired. +Developers use this event to know the language changed, and might do the following: + +- Update the content of extension pages in place (without refreshing the page) +- Reload extension pages: + - If the page is stateless, refresh the page directly. + - If the page is stateful, save the current state and then refresh the page. + - Prompts the user that the language setting has changed, and asks the user if they want to refresh the page immediately. +- Re-create the extension context menus, since the title of the context menus shoule be updated. +- Update the action title and badge text if needed. + +#### Behavior of `i18n.getMessage()`, manifest and CSS files + +When this proposal is implemented, the behavior of these existing functions will change as follows: + +- If the preferred language is not set by `i18n.setCurrentLanguage()`, it is consistent with the existing behavior. +- If the preferred language is set by `i18n.setCurrentLanguage()`, and the extension supports this language, then prioritize using that language. +- If the preferred language is set by `i18n.setCurrentLanguage()`, but the extension doesn't support this language, then treat as if the preferred language is not set. This is an edge case, for example, the language was removed when the extension was upgraded. + +### Browser built-in language selection menu + +Based on the capabilities provided by this proposal, browsers could provide a unified built-in language selection menu for extensions, like Android and iOS. +Whether to provide the built-in language selection menu and how to implement it is up to the browser to decide. This is just a suggestion. + +The built-in language selection menu has the following benefits: +- Easy for developers: Developers do not need to implement it themselves, they only need to adapt to this proposal. +- Easy for users: A unified UI makes it easier for users to use, otherwise each extension might be set up differently. +- It is convenient for developers to develop and test i18n functions by switching the extension language. + +### Integrate other i18n implementations (frameworks) with browser.i18n + +This proposal can also be integrated with third-party i18n frameworks, as these frameworks typically allow developers to specify the language to be used. +For example, developers use `i18n.getCurrentLanguage()` and `i18n.setCurrentLanguage()` for the user preferred language, but don't use `i18n.getMessage()`. + +```js +otherI18nFramework.setLanguage(browser.i18n.getCurrentLanguage()); + +let text = otherI18nFramework.getMessage(key); +``` + +### New Permissions + +N/A + +### Manifest File Changes + +If the browser would like to support the built-in language selection menu for extensions, the manifest file should add a new key for developers to opt-in this feature, like the following: +``` +{ + "name": "__MSG_extName__", + "default_locale": "en", + "builtin_languages_menu": true | false(default) +} +``` + +## Security and Privacy + +### Exposed Sensitive Data + +N/A + +### Abuse Mitigations + +N/A + +### Additional Security Considerations + +N/A + +## Alternatives + +### Existing Workarounds + +##### Workaround-1: fetch message files + +This is not ideal. Developers need to solve many problems on their own, such as saving the preferred language, implementing placeholders, and fallback mechanisms. + +##### Workaround-2: use other i18n frameworks or implement one yourself + +There are some JavaScript i18n frameworks that provide similar features, which allow developers to specify the language they want to use. +In fact, this workaround is equivalent to asking developers to give up using `browser.i18n`. +In addition, developers need to save the preferred language by themself. + +##### Workaround-3: mix browser.i18n and other workarounds. + +Because some text can only be localized by `browser.i18n`, such as the extension name and description, developers often mix different implementations. + +### Open Web API + +The Web provides some related tools, such as `Intl`, but there is no unified framework that provides functionality such as `browser.i18n.getMessage()`. +There are some third-party i18n frameworks, but they use custom mechanisms rather than the `browser.i18n` mechanism. + +## Implementation Notes + +When the extension is upgraded, if the new version removes the language that was set by `i18n.setCurrentLanguage(code)`, the user preferred language should be reverted to the unset state. + +## Future Work + +N/A From ab186cf75c4e8e5612c4cf28a7c526d474e3c822 Mon Sep 17 00:00:00 2001 From: "Jackie(Guokai) Han" Date: Thu, 20 Jun 2024 22:55:12 +0800 Subject: [PATCH 02/10] Fix a grammatical issue Co-authored-by: carlosjeurissen <1038267+carlosjeurissen@users.noreply.github.com> --- proposals/per-extension-language-preferences.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/per-extension-language-preferences.md b/proposals/per-extension-language-preferences.md index 9399c594..c105a632 100644 --- a/proposals/per-extension-language-preferences.md +++ b/proposals/per-extension-language-preferences.md @@ -85,8 +85,8 @@ This method return the language that the extension is displayed in. - If the extension doesn't use `browser.i18n` (there is no "_locales" directory), return `undefined`. - If the preferred language is not set by `i18n.setCurrentLanguage()`, returns the current language used by `i18n.getMessage()`, assuming that all languages support all possible keys. -- If the preferred language is set by `i18n.setCurrentLanguage()`, and the extension supports this language, then return this language. -- If the preferred language is set by `i18n.setCurrentLanguage()`, but the extension doesn't support this language (no message file for this language), then treat as if the preferred language is not set. This is an edge case, for example, the language was removed when the extension was upgraded. +- If the preferred language has been set by `i18n.setCurrentLanguage()`, and the extension supports this language, then return this language. +- If the preferred language has been set by `i18n.setCurrentLanguage()`, but the extension doesn't support this language (no message file for this language), then treat as if the preferred language is not set. This is an edge case, for example, the language was removed when the extension was upgraded. ##### Use Case 1: use the extension displayed language with other locale-related APIs. From e7a2d5efa8422d09496dfda7ab85d28cc8bead25 Mon Sep 17 00:00:00 2001 From: "Jackie(Guokai) Han" Date: Thu, 27 Jun 2024 20:15:37 +0800 Subject: [PATCH 03/10] move the "Browser built-in language selection menu" to the "Future Work" section --- .../per-extension-language-preferences.md | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/proposals/per-extension-language-preferences.md b/proposals/per-extension-language-preferences.md index c105a632..d7ac10f6 100644 --- a/proposals/per-extension-language-preferences.md +++ b/proposals/per-extension-language-preferences.md @@ -152,16 +152,6 @@ When this proposal is implemented, the behavior of these existing functions will - If the preferred language is set by `i18n.setCurrentLanguage()`, and the extension supports this language, then prioritize using that language. - If the preferred language is set by `i18n.setCurrentLanguage()`, but the extension doesn't support this language, then treat as if the preferred language is not set. This is an edge case, for example, the language was removed when the extension was upgraded. -### Browser built-in language selection menu - -Based on the capabilities provided by this proposal, browsers could provide a unified built-in language selection menu for extensions, like Android and iOS. -Whether to provide the built-in language selection menu and how to implement it is up to the browser to decide. This is just a suggestion. - -The built-in language selection menu has the following benefits: -- Easy for developers: Developers do not need to implement it themselves, they only need to adapt to this proposal. -- Easy for users: A unified UI makes it easier for users to use, otherwise each extension might be set up differently. -- It is convenient for developers to develop and test i18n functions by switching the extension language. - ### Integrate other i18n implementations (frameworks) with browser.i18n This proposal can also be integrated with third-party i18n frameworks, as these frameworks typically allow developers to specify the language to be used. @@ -179,14 +169,7 @@ N/A ### Manifest File Changes -If the browser would like to support the built-in language selection menu for extensions, the manifest file should add a new key for developers to opt-in this feature, like the following: -``` -{ - "name": "__MSG_extName__", - "default_locale": "en", - "builtin_languages_menu": true | false(default) -} -``` +N/A ## Security and Privacy @@ -231,4 +214,21 @@ When the extension is upgraded, if the new version removes the language that was ## Future Work -N/A +### Browser built-in language selection menu + +Based on the capabilities provided by this proposal, browsers could provide a unified built-in language selection menu for extensions, like Android and iOS. +Whether to provide the built-in language selection menu and how to implement it is up to the browser to decide. + +The built-in language selection menu has the following benefits: +- Easy for developers: Developers do not need to implement it themselves, they only need to adapt to this proposal. +- Easy for users: A unified UI makes it easier for users to use, otherwise each extension might be set up differently. +- It is convenient for developers to develop and test i18n functions by switching the extension language. + +If the browser would like to support the built-in language selection menu for extensions, the manifest file should add a new key for developers to opt-in this feature, like the following: +``` +{ + "name": "__MSG_extName__", + "default_locale": "en", + "builtin_languages_menu": true | false(default) +} +``` From a12f54580f17377c1d2a3d6469579e4437ee0252 Mon Sep 17 00:00:00 2001 From: "Jackie(Guokai) Han" Date: Thu, 27 Jun 2024 20:45:03 +0800 Subject: [PATCH 04/10] rephrase text about updating browser UI and extension UI --- proposals/per-extension-language-preferences.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/proposals/per-extension-language-preferences.md b/proposals/per-extension-language-preferences.md index d7ac10f6..7c8a5403 100644 --- a/proposals/per-extension-language-preferences.md +++ b/proposals/per-extension-language-preferences.md @@ -116,7 +116,9 @@ This method sets the user preferred language for this extension. 1. trigger `i18n.onLanguageChanged` event. In addition, when `i18n.setCurrentLanguage(code)` success: -- the browser should update related browser UI and extension UI, because some values in manifest.json may be changed, like `name`, `short_name`, `description` and `action.default_title`. +- Because some values in manifest.json may be changed, like `name`, `short_name`, `description` and `action.default_title`: + - The browser should update related extension UI, e.g. `action.default_title`. + - The browser might update related browser UI, but this part is up to the browser to decide. - Text from `i18n.getMessage()` and css files don't update. These are handled by developers. ##### Persistence of `i18n.setCurrentLanguage(code)` From 4871ecc5e8f966942e03049afe19fa83d8f0afb9 Mon Sep 17 00:00:00 2001 From: "Jackie(Guokai) Han" Date: Thu, 27 Jun 2024 20:52:55 +0800 Subject: [PATCH 05/10] remove 1 line description for setCurrentLanguage --- proposals/per-extension-language-preferences.md | 1 - 1 file changed, 1 deletion(-) diff --git a/proposals/per-extension-language-preferences.md b/proposals/per-extension-language-preferences.md index 7c8a5403..f4f22f23 100644 --- a/proposals/per-extension-language-preferences.md +++ b/proposals/per-extension-language-preferences.md @@ -107,7 +107,6 @@ const today_date = new Date().toLocaleString(); // format date string in 'en-US' This method sets the user preferred language for this extension. -- If the extension doesn't use `browser.i18n` (there is no "_locales" directory), return a rejected Promise. - If the code is an invalid language tag or an unsupported language by this extension, return a rejected Promise. - If the code is null, revert to the unset state. - else do the following: From b50bcc8ccf0493157413468cddf75085d83c266f Mon Sep 17 00:00:00 2001 From: "Jackie(Guokai) Han" Date: Mon, 29 Jul 2024 15:22:17 +0800 Subject: [PATCH 06/10] add "Content Scripts Availability" in the Behavior section --- proposals/per-extension-language-preferences.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/proposals/per-extension-language-preferences.md b/proposals/per-extension-language-preferences.md index f4f22f23..7523bca2 100644 --- a/proposals/per-extension-language-preferences.md +++ b/proposals/per-extension-language-preferences.md @@ -79,6 +79,17 @@ i18n.onLanguageChanged.addListener(callback) ### Behavior +#### Content Scripts Availability + +In history, all `browser.i18n` APIs were available in content scripts. But some of methods in this proposal should not be available in content scripts because of their purpose. + +| Method | Content Scripts Availability | +| ------------- | ------------- | +| `getCurrentLanguage()` | Yes | +| `setCurrentLanguage()` | No | +| `getAllLanguages()` | No | +| `onLanguageChanged()` | Yes | + #### Behavior of `i18n.getCurrentLanguage()` This method return the language that the extension is displayed in. From 510a0fc54618ad5477586ea6cd04d05477078027 Mon Sep 17 00:00:00 2001 From: "Jackie(Guokai) Han" Date: Mon, 29 Jul 2024 15:53:57 +0800 Subject: [PATCH 07/10] add A New Predefined Message `@@current_locale` --- proposals/per-extension-language-preferences.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/proposals/per-extension-language-preferences.md b/proposals/per-extension-language-preferences.md index 7523bca2..4dc99d59 100644 --- a/proposals/per-extension-language-preferences.md +++ b/proposals/per-extension-language-preferences.md @@ -90,6 +90,11 @@ In history, all `browser.i18n` APIs were available in content scripts. But some | `getAllLanguages()` | No | | `onLanguageChanged()` | Yes | +#### A New Predefined Message `@@current_locale` +There is an exsiting predefined message `@@ui_locale`, that reflects the value of `i18n.getUILanguage()`, but the value uses underscore (e.g. "en_US") as separator in Chrome and hyphen (e.g. "en-US") in Firefox. + +Relative to `@@ui_locale`, a new predefined message, ``@@current_locale``, should be added, which reflects the value of `i18n.getCurrentLanguage()`. + #### Behavior of `i18n.getCurrentLanguage()` This method return the language that the extension is displayed in. From 269c07f1c1ab331021558ec2a16fa03f00a5e246 Mon Sep 17 00:00:00 2001 From: "Jackie(Guokai) Han" Date: Mon, 29 Jul 2024 16:27:30 +0800 Subject: [PATCH 08/10] Add supplementary note of Abuse Mitigations --- proposals/per-extension-language-preferences.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/per-extension-language-preferences.md b/proposals/per-extension-language-preferences.md index 4dc99d59..9a36d9dd 100644 --- a/proposals/per-extension-language-preferences.md +++ b/proposals/per-extension-language-preferences.md @@ -196,7 +196,7 @@ N/A ### Abuse Mitigations -N/A +A case of abuse is changing the extension name/description dynamically by `i18n.setCurrentLanguage()`. This is not a security threat because all metadata published in the extension store is audited. And for the extension management page (e.g. chrome://extensions/), the extension name should be displayed in the same language as the management page (i.e. the Browser UI language), so it is not affected by `i18n.setCurrentLanguage()`. ### Additional Security Considerations From f5ec0179a7ed5170611ec7dc684d59f03bb11ba3 Mon Sep 17 00:00:00 2001 From: "Jackie(Guokai) Han" Date: Wed, 7 Aug 2024 21:22:21 +0800 Subject: [PATCH 09/10] Applied text revision for Content Scripts Availability Co-authored-by: Rob Wu --- proposals/per-extension-language-preferences.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/per-extension-language-preferences.md b/proposals/per-extension-language-preferences.md index 9a36d9dd..2f12e3e2 100644 --- a/proposals/per-extension-language-preferences.md +++ b/proposals/per-extension-language-preferences.md @@ -81,14 +81,14 @@ i18n.onLanguageChanged.addListener(callback) #### Content Scripts Availability -In history, all `browser.i18n` APIs were available in content scripts. But some of methods in this proposal should not be available in content scripts because of their purpose. +Unlike other APIs, all `browser.i18n` APIs are historically available to content scripts. To counter abuse, some of the features in this proposal should not be available in content scripts. -| Method | Content Scripts Availability | +| API | Content Scripts Availability | | ------------- | ------------- | | `getCurrentLanguage()` | Yes | | `setCurrentLanguage()` | No | | `getAllLanguages()` | No | -| `onLanguageChanged()` | Yes | +| `onLanguageChanged` | Yes | #### A New Predefined Message `@@current_locale` There is an exsiting predefined message `@@ui_locale`, that reflects the value of `i18n.getUILanguage()`, but the value uses underscore (e.g. "en_US") as separator in Chrome and hyphen (e.g. "en-US") in Firefox. From 30d9953bf045fb1e8d5447f331bff1ec4ae1ee5f Mon Sep 17 00:00:00 2001 From: "Jackie(Guokai) Han" Date: Wed, 7 Aug 2024 21:27:38 +0800 Subject: [PATCH 10/10] Apply "Abuse Mitigations" revision Co-authored-by: Rob Wu --- proposals/per-extension-language-preferences.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/per-extension-language-preferences.md b/proposals/per-extension-language-preferences.md index 2f12e3e2..28daa08e 100644 --- a/proposals/per-extension-language-preferences.md +++ b/proposals/per-extension-language-preferences.md @@ -196,7 +196,7 @@ N/A ### Abuse Mitigations -A case of abuse is changing the extension name/description dynamically by `i18n.setCurrentLanguage()`. This is not a security threat because all metadata published in the extension store is audited. And for the extension management page (e.g. chrome://extensions/), the extension name should be displayed in the same language as the management page (i.e. the Browser UI language), so it is not affected by `i18n.setCurrentLanguage()`. +A case of abuse is changing the extension name/description dynamically by `i18n.setCurrentLanguage()` in an attempt to confuse the user and hinder their ability to identify the extension. A mitigation to this issue is to ignore changes from `setCurrentLanguage` in the extension management parts of the browser UI, and always use the browser's configured UI language instead. This includes extension management pages (e.g. `chrome://extensions` in Chrome and `about:addons` in Firefox) and permission dialogs. ### Additional Security Considerations