├── .gitignore ├── background.js ├── README.md ├── manifest.json ├── LICENSE ├── options.js ├── options.html ├── rules.js └── icon.svg /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | *.zip -------------------------------------------------------------------------------- /background.js: -------------------------------------------------------------------------------- 1 | import { RuleSet, getRules } from "./rules.js"; 2 | 3 | async function applyRulesFromStorage() { 4 | const rules = new RuleSet(await getRules()); 5 | rules.registerAll(); 6 | } 7 | 8 | browser.runtime.onInstalled.addListener(async (details) => { 9 | if (details.reason === 'update') { 10 | console.info('Addon updated; reload rules.'); 11 | await applyRulesFromStorage(); 12 | } 13 | }); 14 | 15 | browser.storage.local.onChanged.addListener(async () => { 16 | console.debug("Ruleset changed"); 17 | await applyRulesFromStorage();; 18 | }); 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Accept-Language per site 2 | 3 | Firefox extension to change `Accept-Language` header for different websites. 4 | 5 | ## Requirement 6 | 7 | Current version: Firefox 113 or above 8 | 9 | v0.2.x (`mv2` branch): Firefox 48 or above 10 | 11 | 12 | ## Usage 13 | 14 | 1. [Install it](https://addons.mozilla.org/firefox/addon/accept-language-per-site/). 15 | 2. Go to `about:addons` page, open options, add domain name 16 | (e.g. `*.example.com`) and languages (e.g. `en`). 17 | 3. Click save button. 18 | 19 | ## Acknowledgement 20 | The icon is derived from [Material icons](https://material.io/icons/). -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 3, 3 | "name": "Accept-Language per site", 4 | "version": "0.4.0", 5 | "homepage_url": "https://github.com/sorz/accept-language-per-site", 6 | "author": "Shell Chen", 7 | "description": "Change Accept-Language for different sites", 8 | "browser_specific_settings": { 9 | "gecko": { 10 | "id": "addon-accept-lang@sorz.org", 11 | "strict_min_version": "128.0" 12 | } 13 | }, 14 | "icons": { 15 | "48": "icon.svg", 16 | "96": "icon.svg" 17 | }, 18 | 19 | "permissions": [ 20 | "declarativeNetRequestWithHostAccess", 21 | "storage" 22 | ], 23 | 24 | "optional_host_permissions": [ 25 | "*://*/*" 26 | ], 27 | 28 | "background": { 29 | "scripts": ["background.js"], 30 | "type": "module" 31 | }, 32 | 33 | "options_ui": { 34 | "page": "options.html" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Shell Chen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /options.js: -------------------------------------------------------------------------------- 1 | import { Rule, getRules } from "./rules.js"; 2 | 3 | const $ = selector => document.querySelector(selector); 4 | const sleep = ms => new Promise(cb => setTimeout(cb, ms)); 5 | 6 | async function saveOptions(ev) { 7 | const button = $("button[type='submit']"); 8 | ev.preventDefault(); 9 | button.disabled = true; 10 | try { 11 | // Get rules from form 12 | const list = document.querySelectorAll("#list li"); 13 | const rules = Array.from(list) 14 | .map(li => ({ 15 | host: li.querySelector(".host").value, 16 | language: li.querySelector(".language").value 17 | })) 18 | .filter(rule => rule.host && rule.language) 19 | .map(({host, language}) => new Rule(host, language)); 20 | 21 | // Check permission 22 | const permissions = { 23 | origins: rules.map((rule) => rule.permissionOrigins), 24 | }; 25 | if (!await browser.permissions.request(permissions)) { 26 | throw new Error("permssions rejected"); 27 | } 28 | 29 | // Save rules 30 | await browser.storage.local.set({ rules: rules }); 31 | $("#saved").classList.add("show"); 32 | await sleep(800); 33 | $("#saved").classList.remove("show"); 34 | } catch (err) { 35 | alert(`Failed to save rules: ${err}`) 36 | } finally { 37 | button.disabled = false; 38 | } 39 | } 40 | 41 | async function restoreOptions() { 42 | let rules = await getRules(); 43 | let list = $("#list"); 44 | list.innerHTML = ''; 45 | rules.forEach(rule => list.appendChild(rule.formHTML)); 46 | list.appendChild(new Rule().formHTML); 47 | } 48 | 49 | function addMoreRule() { 50 | $("#list").appendChild(new Rule().formHTML); 51 | } 52 | 53 | document.addEventListener("DOMContentLoaded", restoreOptions); 54 | $("form").addEventListener("submit", saveOptions); 55 | $("#more").addEventListener("click", addMoreRule); 56 | -------------------------------------------------------------------------------- /options.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 21 | 22 | 23 |* - match everything.example.com – match example.com only.*.example.com – match example.com and its subdomains.
41 |