Browse Source

fix: update `chrome.i18n` for Manifest v3 (#39442)

* fix: update `chrome.i18n` for Manifest v3

* chore: remove i18n JSON spec (#39391)
Shelley Vohr 1 year ago
parent
commit
e0b1723dd1

+ 0 - 130
shell/common/extensions/api/i18n.json

@@ -1,130 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-[
-  {
-    "namespace": "i18n",
-    "description": "Use the <code>chrome.i18n</code> infrastructure to implement internationalization across your whole app or extension.",
-    "types": [
-      {
-        "id": "LanguageCode",
-        "type": "string",
-        "description": "An ISO language code such as <code>en</code> or <code>fr</code>. For a complete list of languages supported by this method, see <a href='http://src.chromium.org/viewvc/chrome/trunk/src/third_party/cld/languages/internal/languages.cc'>kLanguageInfoTable</a>. For an unknown language, <code>und</code> will be returned, which means that [percentage] of the text is unknown to CLD"
-      }
-    ],
-    "functions": [
-      {
-        "name": "getAcceptLanguages",
-        "type": "function",
-        "description": "Gets the accept-languages of the browser. This is different from the locale used by the browser; to get the locale, use $(ref:i18n.getUILanguage).",
-        "parameters": [
-          {
-            "type": "function",
-            "name": "callback",
-            "parameters": [
-              {"name": "languages", "type": "array", "items": {"$ref": "LanguageCode"}, "description": "Array of LanguageCode"}
-            ]
-          }
-        ]
-      },
-      {
-        "name": "getMessage",
-        "nocompile": true,
-        "type": "function",
-        "description": "Gets the localized string for the specified message. If the message is missing, this method returns an empty string (''). If the format of the <code>getMessage()</code> call is wrong &mdash; for example, <em>messageName</em> is not a string or the <em>substitutions</em> array has more than 9 elements &mdash; this method returns <code>undefined</code>.",
-        "parameters": [
-          {
-            "type": "string",
-            "name": "messageName",
-            "description": "The name of the message, as specified in the <a href='i18n-messages'><code>messages.json</code></a> file."
-          },
-          {
-            "type": "any",
-            "name": "substitutions",
-            "optional": true,
-            "description": "Up to 9 substitution strings, if the message requires any."
-          },
-          {
-            "type": "object",
-            "name": "options",
-            "optional": true,
-            "properties": {
-              "escapeLt": {
-                "type": "boolean",
-                "optional": true,
-                "description": "Escape <code>&lt;</code> in translation to <code>&amp;lt;</code>. This applies only to the message itself, not to the placeholders. Developers might want to use this if the translation is used in an HTML context. Closure Templates used with Closure Compiler generate this automatically."
-              }
-            }
-          }
-        ],
-        "returns": {
-          "type": "string",
-          "description": "Message localized for current locale."
-        }
-      },
-      {
-        "name": "getUILanguage",
-        "type": "function",
-        "nocompile": true,
-        "description": "Gets the browser UI language of the browser. This is different from $(ref:i18n.getAcceptLanguages) which returns the preferred user languages.",
-        "parameters": [],
-        "returns": {
-          "type": "string",
-          "description": "The browser UI language code such as en-US or fr-FR."
-        }
-      },
-      {
-        "name": "detectLanguage",
-        "type": "function",
-        "nocompile": true,
-        "description": "Detects the language of the provided text using CLD.",
-        "parameters": [
-          {
-            "type": "string",
-            "name": "text",
-            "minimum": 0,
-            "description": "User input string to be translated."
-          },
-          {
-            "type": "function",
-            "name": "callback",
-            "parameters": [
-              {
-                "type": "object",
-                "name": "result",
-                "description": "LanguageDetectionResult object that holds detected language reliability and array of DetectedLanguage",
-                "properties": {
-                  "isReliable": { "type": "boolean", "description": "CLD detected language reliability" },
-                  "languages":
-                    {
-                      "type": "array",
-                      "description": "array of detectedLanguage",
-                      "items":
-                        {
-                          "type": "object",
-                          "description": "DetectedLanguage object that holds detected ISO language code and its percentage in the input string",
-                          "properties":
-                            {
-                              "language":
-                                {
-                                  "$ref": "LanguageCode"
-                                },
-                              "percentage":
-                                {
-                                  "type": "integer",
-                                  "description": "The percentage of the detected language"
-                                }
-                            }
-                        }
-                    }
-                }
-              }
-            ]
-          }
-        ]
-      }
-    ],
-    "events": []
-  }
-]

+ 91 - 1
spec/extensions-spec.ts

@@ -211,7 +211,7 @@ describe('chrome extensions', () => {
     };
     beforeEach(async () => {
       const customSession = session.fromPartition(`persist:${uuid.v4()}`);
-      extension = await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-i18n'));
+      extension = await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-i18n', 'v2'));
       w = new BrowserWindow({ show: false, webPreferences: { session: customSession, nodeIntegration: true, contextIsolation: false } });
       await w.loadURL(url);
     });
@@ -701,5 +701,95 @@ describe('chrome extensions', () => {
       const scope = await registrationPromise;
       expect(scope).equals(extension.url);
     });
+
+    describe('chrome.i18n', () => {
+      let customSession: Session;
+      let w = null as unknown as BrowserWindow;
+
+      before(async () => {
+        customSession = session.fromPartition(`persist:${uuid.v4()}`);
+        await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-i18n', 'v3'));
+      });
+
+      beforeEach(() => {
+        w = new BrowserWindow({
+          show: false,
+          webPreferences: {
+            session: customSession,
+            nodeIntegration: true
+          }
+        });
+      });
+
+      afterEach(closeAllWindows);
+
+      it('getAcceptLanguages', async () => {
+        await w.loadURL(url);
+
+        const message = { method: 'getAcceptLanguages' };
+        w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`);
+
+        const [, , responseString] = await emittedOnce(w.webContents, 'console-message');
+        const response = JSON.parse(responseString);
+
+        expect(response).to.be.an('array').that.is.not.empty('languages array is empty');
+      });
+
+      it('getUILanguage', async () => {
+        await w.loadURL(url);
+
+        const message = { method: 'getUILanguage' };
+        w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`);
+
+        const [, , responseString] = await emittedOnce(w.webContents, 'console-message');
+        const response = JSON.parse(responseString);
+
+        expect(response).to.be.a('string');
+      });
+
+      it('getMessage', async () => {
+        await w.loadURL(url);
+
+        const message = { method: 'getMessage' };
+        w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`);
+
+        const [, , responseString] = await emittedOnce(w.webContents, 'console-message');
+        const response = JSON.parse(responseString);
+
+        expect(response).to.equal('Hola mundo!!');
+      });
+
+      it('detectLanguage', async () => {
+        await w.loadURL(url);
+
+        const greetings = [
+          'Ich liebe dich', // German
+          'Mahal kita', // Filipino
+          '愛してます', // Japanese
+          'دوستت دارم', // Persian
+          'Minä rakastan sinua' // Finnish
+        ];
+        const message = { method: 'detectLanguage', args: [greetings] };
+        w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`);
+
+        const [, , responseString] = await emittedOnce(w.webContents, 'console-message');
+        const response = JSON.parse(responseString);
+
+        expect(response).to.be.an('array');
+
+        for (const item of response) {
+          expect(Object.keys(item)).to.deep.equal(['isReliable', 'languages']);
+        }
+
+        const languages = response.map((r: { isReliable: boolean, languages: any[] }) => r.languages[0]);
+        expect(languages).to.deep.equal([
+          { language: 'de', percentage: 100 },
+          { language: 'fil', percentage: 100 },
+          { language: 'ja', percentage: 100 },
+          { language: 'ps', percentage: 100 },
+          { language: 'fi', percentage: 100 }
+        ]);
+      });
+    });
   });
 });

+ 0 - 0
spec/fixtures/extensions/chrome-i18n/_locales/en/messages.json → spec/fixtures/extensions/chrome-i18n/v2/_locales/en/messages.json


+ 0 - 0
spec/fixtures/extensions/chrome-i18n/main.js → spec/fixtures/extensions/chrome-i18n/v2/main.js


+ 0 - 0
spec/fixtures/extensions/chrome-i18n/manifest.json → spec/fixtures/extensions/chrome-i18n/v2/manifest.json


+ 6 - 0
spec/fixtures/extensions/chrome-i18n/v3/_locales/es/messages.json

@@ -0,0 +1,6 @@
+{
+  "extName": {
+    "message": "Hola mundo!!",
+    "description": "Nombre de extensión"
+  }
+}

+ 36 - 0
spec/fixtures/extensions/chrome-i18n/v3/main.js

@@ -0,0 +1,36 @@
+/* global chrome */
+
+chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
+  sendResponse(request);
+});
+
+const map = {
+  getAcceptLanguages () {
+    chrome.i18n.getAcceptLanguages().then((languages) => {
+      console.log(JSON.stringify(languages));
+    });
+  },
+  getMessage () {
+    const message = chrome.i18n.getMessage('extName');
+    console.log(JSON.stringify(message));
+  },
+  getUILanguage () {
+    const language = chrome.i18n.getUILanguage();
+    console.log(JSON.stringify(language));
+  },
+  async detectLanguage (texts) {
+    const result = [];
+    for (const text of texts) {
+      const language = await chrome.i18n.detectLanguage(text);
+      result.push(language);
+    }
+    console.log(JSON.stringify(result));
+  }
+};
+
+const dispatchTest = (event) => {
+  const { method, args = [] } = JSON.parse(event.data);
+  map[method](...args);
+};
+
+window.addEventListener('message', dispatchTest, false);

+ 17 - 0
spec/fixtures/extensions/chrome-i18n/v3/manifest.json

@@ -0,0 +1,17 @@
+{
+  "name": "chrome-i18n",
+  "version": "1.0",
+  "default_locale": "es",
+  "content_scripts": [
+    {
+      "matches": [
+        "<all_urls>"
+      ],
+      "js": [
+        "main.js"
+      ],
+      "run_at": "document_start"
+    }
+  ],
+  "manifest_version": 3
+}