diff --git a/.eslintrc.js b/.eslintrc.js
index 8ff448efd..f91394cce 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -2,19 +2,18 @@ module.exports = {
root: true,
env: {
browser: true,
- node: true
+ node: true,
},
parserOptions: {
- parser: 'babel-eslint'
+ parser: 'babel-eslint',
},
extends: [
'@nuxtjs',
'prettier',
'prettier/vue',
'plugin:prettier/recommended',
- 'plugin:nuxt/recommended'
- ],
- plugins: [
- 'prettier'
+ 'plugin:nuxt/recommended',
+ 'plugin:json/recommended',
],
+ plugins: ['prettier'],
}
diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml
new file mode 100644
index 000000000..e9041ab8f
--- /dev/null
+++ b/.github/workflows/validate.yml
@@ -0,0 +1,25 @@
+name: Validate
+on:
+ push:
+ pull_request:
+
+jobs:
+ validate:
+ name: Validate
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2.3.3
+ - uses: actions/setup-node@v2.1.2
+ with:
+ node-version: '14'
+ - name: Restore npm cache
+ uses: actions/cache@v2
+ with:
+ path: ~/.npm
+ key: ${{ runner.os }}-node-${{ hashFiles('**/package.json') }}
+ restore-keys: |
+ ${{ runner.os }}-node-
+ - name: Install dependencies
+ run: npm install
+ - name: Validate
+ run: npm run validate
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 000000000..253e4a11f
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,37 @@
+# Contributing
+
+Wappalyzer is an [MIT-licensed](https://github.com/aliasio/wappalyzer/blob/master/LICENSE), open source project written in JavaScript. Anyone is welcome to contribute.
+
+## Getting started
+
+To get started, see the [README](https://github.com/aliasio/wappalyzer/blob/master/README.md).
+
+## Submitting changes
+
+- First, run `yarn run validate` to identify any issues.
+- Use descriptive commit messages, e.g. 'Add WordPress detection'.
+- Push your commits to a new branch on your own fork.
+- Finally, submit a [pull request](https://help.github.com/articles/about-pull-requests/) and describe your changes.
+
+## Adding a new technology
+
+Wappalyzer uses [regular expressions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) to fingerprint technologies. Refer to the [specification](https://github.com/AliasIO/wappalyzer/blob/master/README.md#specification) for detail.
+
+- Add a new block to [`src/technologies.json`](https://github.com/aliasio/wappalyzer/blob/master/src/technologies.json).
+- Add an icon to [`src/drivers/webextension/images/icons`](https://github.com/aliasio/wappalyzer/tree/master/src/drivers/webextension/images/icons). The image must be square, either SVG or PNG (32 x 32 pixels).
+
+Only widely used technologies are accepted. When creating a pull request, include ten or more links to websites that use the application, a GitHub page with at least 1,000 stars or anything that will help establish the size of the user base.
+
+## Adding a new category
+
+Please [open an issue on GitHub](https://github.com/aliasio/wappalyzer/issues) first to discuss the need for a new category.
+
+To add a category, edit [`src/technologies.json`](https://github.com/aliasio/wappalyzer/blob/master/src/technologies.json) and update every [locale](https://github.com/aliasio/wappalyzer/tree/master/src/drivers/webextension/_locales). You may use the English category name in all of them.
+
+## Adding a new translation
+
+To add a new translation, copy the `en` folder in [`src/drivers/webextension/_locales`](https://github.com/aliasio/wappalyzer/tree/master/src/drivers/webextension/_locales), rename it to the relevant two-letter country code and update the containing `messages.json` file.
+
+## Adding a new feature
+
+Please [open an issue on GitHub](https://github.com/aliasio/wappalyzer/issues) first. New features and large changes are rarely accepted without prior discussion.
diff --git a/README.md b/README.md
index 97fdd7fc3..7694a4e59 100644
--- a/README.md
+++ b/README.md
@@ -1,21 +1,21 @@
# Wappalyzer [![Travis](https://travis-ci.org/aliasio/wappalyzer.svg?branch=master)](https://travis-ci.org/aliasio/wappalyzer/)
-[Wappalyzer](https://www.wappalyzer.com) identifies technologies on websites.
-It detects content management systems, ecommerce platforms, JavaScript frameworks,
-analytics tools and [much more](https://www.wappalyzer.com/technologies).
+[Wappalyzer](https://www.wappalyzer.com) identifies technologies on websites, including content management systems, ecommerce platforms, JavaScript frameworks, analytics tools and [much more](https://www.wappalyzer.com/technologies).
* [wappalyzer on NPM](https://www.npmjs.com/package/wappalyzer)
* [wappalyzer-core on NPM](https://www.npmjs.com/package/wappalyzer-core)
* [Chrome extension](https://chrome.google.com/webstore/detail/wappalyzer/gppongmhjkpfnbhagpmjfkannfbllamg)
* [Firefox add-on](https://addons.mozilla.org/en-US/firefox/addon/wappalyzer/)
* [Edge extension](https://microsoftedge.microsoft.com/addons/detail/mnbndgmknlpdjdnjfmfcdjoegcckoikn)
-* [Bookmarklet](https://www.wappalyzer.com/download)
-* [wappalyzer/cli on Docker Hub](https://hub.docker.com/r/wappalyzer/cli/)
+* [Safari extension](https://apps.apple.com/app/wappalyzer/id1520333300)
+* [All apps and integrations](https://www.wappalyzer.com/api/download)
* [Wappalyzer REST APIs](https://www.wappalyzer.com/api/)
-## Documentation
+## Prerequisites
-Please read the [developer documentation](https://www.wappalyzer.com/docs).
+- [Git](https://git-scm.com)
+- [Node.js](https://nodejs.org) version 12 or higher
+- [Yarn](https://yarnpkg.com)
## Quick start
@@ -46,3 +46,430 @@ node src/drivers/npm/cli.js https://example.com
* Go go `about:debugging#/runtime/this-firefox`
* Click 'Load Temporary Add-on'
* Select `src/drivers/webextension/manifest.json`
+
+## Specification
+
+A long list of [regular expressions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) is used to identify technologies on web pages. Wappalyzer inspects HTML code, as well as JavaScript variables, response headers and more.
+
+Patterns (regular expressions) are kept in [`src/technologies.json`](https://github.com/aliasio/wappalyzer/blob/master/src/technologies.json). The following is an example of an application fingerprint.
+
+#### Example
+
+```json
+"Example": {
+ "description": "A short description of the technology.",
+ "cats": [
+ "1"
+ ],
+ "cookies": {
+ "cookie_name": "Example"
+ },
+ "dom": {
+ "#example-id": {
+ "attributes": {
+ "class": "example-class"
+ },
+ "properties": {
+ "example-property": ""
+ },
+ "text": "Example text content"
+ }
+ },
+ "dns": {
+ "MX": [
+ "example\\.com"
+ ]
+ },
+ "js": {
+ "Example.method": ""
+ },
+ "excludes": "Example",
+ "headers": {
+ "X-Powered-By": "Example"
+ },
+ "html": " ]example\\.css",
+ "css": "\\.example-class",
+ "robots": "Disallow: /unique-path/",
+ "implies": "PHP\\;confidence:50",
+ "meta": {
+ "generator": "(?:Example|Another Example)"
+ },
+ "script": "example-([0-9.]+)\\.js\\;confidence:50\\;version:\\1",
+ "url": "example\\.com",
+ "xhr": "example\\.com",
+ "oss": true,
+ "saas": true,
+ "pricing": ["medium", "freemium", "recurring"],
+ "website": "https://example.com",
+}
+```
+
+## JSON fields
+
+Find the JSON schema at [`schema.json`](https://github.com/aliasio/wappalyzer/blob/master/schema.json).
+
+### Required properties
+
+
+
+
+ Field
+ Type
+ Description
+ Example
+
+
+
+
+ cats
+ Array
+
+ One or more category IDs.
+
+ [1, 6]
+
+
+ website
+ String
+ URL of the application's website.
+
+ "https://example.com"
+
+
+
+
+
+### Optional properties
+
+
+
+
+ Field
+ Type
+ Description
+ Example
+
+
+
+
+ description
+ String
+
+ A short description of the technology in British English (max.
+ 250 characters). Write in a neutral, factual tone; not like an
+ ad.
+
+ "A short description."
+
+
+ icon
+ String
+ Application icon filename.
+ "WordPress.svg"
+
+
+ cpe
+ String
+
+ The
+ CPE
+ is a structured naming scheme for applications, see the
+ specification .
+
+ "cpe:/a:apache:http_server"
+
+
+ saas
+ Boolean
+
+ The technology is offered as a Software-as-a-Service (SaaS), i.e. hosted or cloud-based.
+
+ true
+
+
+ oss
+ Boolean
+
+ The technology has an open-source license.
+
+ true
+
+
+ pricing
+ Array
+
+Cost indicator (based on a typical plan or average monthly price) and available pricing models. For paid products only.
+
+One of:
+
+ low
Up to US 100 / mo
+ mid
Up US 1,000 / mo
+ high
More than 10,000 / mo
+
+
+Plus any of:
+
+ freemium
Free plan available
+ onetime
One-time payments accepted
+ recurring
Subscriptions available
+ poa
Price on asking
+ payg
Pay as you go (e.g. commissions or usage-based fees)
+
+
+ ["low", "freemium"]
+
+
+
+
+### Implies and excludes (optional)
+
+
+
+
+
+ Field
+ Type
+ Description
+ Example
+
+
+
+
+ implies
+ String | Array
+
+ The presence of one application can imply the presence of
+ another, e.g. WordpPress means PHP is also in use.
+
+ "PHP"
+
+
+ excludes
+ String | Array
+
+ Opposite of implies. The presence of one application can exclude
+ the presence of another.
+
+ "Apache"
+
+
+
+
+### Patterns (optional)
+
+
+
+
+ Field
+ Type
+ Description
+ Example
+
+
+
+
+ cookies
+ Object
+ Cookies.
+ { "cookie_name": "Cookie value" }
+
+
+ dom
+ Object
+
+ Uses a
+ query selector
+ to inspect element properties, attributes and text content.
+
+
+ { "#example-id": { "property": { "example-prop": "" } }
+ }
+
+
+
+ dns
+ Object
+
+ DNS records: supports MX, TXT, SOA and NS (NPM driver only).
+
+
+ { "MX": "example\\.com" }
+
+
+
+ js
+ Object
+
+ JavaScript properties (case sensitive). Avoid short property
+ names to prevent matching minified code.
+
+ { "jQuery.fn.jquery": "" }
+
+
+ headers
+ Object
+ HTTP response headers.
+ { "X-Powered-By": "^WordPress$" }
+
+
+ html
+ String | Array
+
+ HTML source code. Patterns must include an HTML opening tag to
+ avoid matching plain text. For performance reasons, avoid
+ html
where possible and use
+ dom
instead.
+
+ "<a [^>]*href=\"index.html"
+
+
+ css
+ String | Array
+
+ CSS rules. Unavailable when a website enforces a same-origin
+ policy. For performance reasons, only a portion of the available
+ CSS rules are used to find matches.
+
+ "\\.example-class"
+
+
+ robots
+ String | Array
+
+ Robots.txt contents.
+
+ "Disallow: /unique-path/"
+
+
+ url
+ String
+ Full URL of the page.
+ "^https?//.+\\.wordpress\\.com"
+
+
+ xhr
+ String
+ Hostnames of XHR requests.
+ "cdn\\.netlify\\.com"
+
+
+ meta
+ Object
+ HTML meta tags, e.g. generator.
+ { "generator": "^WordPress$" }
+
+
+ scripts
+ String | Array
+
+ URLs of JavaScript files included on the page.
+
+ "jquery\\.js"
+
+
+
+
+## Patterns
+
+Patterns are essentially JavaScript regular expressions written as strings, but with some additions.
+
+### Quirks and pitfalls
+
+- Because of the string format, the escape character itself must be escaped when using special characters such as the dot (`\\.`). Double quotes must be escaped only once (`\"`). Slashes do not need to be escaped (`/`).
+- Flags are not supported. Regular expressions are treated as case-insensitive.
+- Capture groups (`()`) are used for version detection. In other cases, use non-capturing groups (`(?:)`).
+- Use start and end of string anchors (`^` and `$`) where possible for optimal performance.
+- Short or generic patterns can cause applications to be identified incorrectly. Try to find unique strings to match.
+
+### Tags
+
+Tags (a non-standard syntax) can be appended to patterns (and implies and excludes, separated by `\\;`) to store additional information.
+
+
+
+
+
+ Tag
+ Description
+ Example
+
+
+
+
+ confidence
+
+ Indicates a less reliable pattern that may cause false
+ positives. The aim is to achieve a combined confidence of 100%.
+ Defaults to 100% if not specified.
+
+
+ "js": { "Mage": "\\;confidence:50" }
+
+
+
+ version
+
+ Gets the version number from a pattern match using a special
+ syntax.
+
+
+ "scripts": "jquery-([0-9.]+)\.js\\;version:\\1"
+
+
+
+
+
+### Version syntax
+
+Application version information can be obtained from a pattern using a capture group. A condition can be evaluated using the ternary operator (`?:`).
+
+
+
+
+
+ Example
+ Description
+
+
+
+
+ \\1
+ Returns the first match.
+
+
+ \\1?a:
+
+ Returns a if the first match contains a value, nothing
+ otherwise.
+
+
+
+ \\1?a:b
+
+ Returns a if the first match contains a value, b otherwise.
+
+
+
+ \\1?:b
+
+ Returns nothing if the first match contains a value, b
+ otherwise.
+
+
+
+ foo\\1
+
+ Returns foo with the first match appended.
+
+
+
+
diff --git a/bin/convert.js b/bin/convert.js
new file mode 100644
index 000000000..735630988
--- /dev/null
+++ b/bin/convert.js
@@ -0,0 +1,137 @@
+const fs = require('fs')
+const path = require('path')
+const { convertFile } = require('convert-svg-to-png')
+
+const appPaths = () => {
+ const fileDir = path.dirname(require.main.filename).split('/')
+ // Remove current bin directory
+ fileDir.pop()
+ const appDir = fileDir.join('/')
+
+ return {
+ basePath: fileDir,
+ appPath: appDir,
+ iconPath: appDir + '/src/drivers/webextension/images/icons',
+ convertPath: appDir + '/src/drivers/webextension/images/icons/converted',
+ }
+}
+
+/**
+ * Copy files from source to destination.
+ * @param source
+ * @param destination
+ */
+function copyFiles(source, destination) {
+ // File destination will be created or overwritten by default.
+ fs.copyFileSync(source, destination)
+ // console.log(`${source} -> ${destination}`)
+}
+
+/**
+ * Get extension of image file.
+ * @returns {string}
+ */
+function getFileExtension(filePath) {
+ return path.extname(filePath)
+}
+
+/**
+ * Get base name of image file.
+ * @returns {string}
+ */
+function getFileName(filePath) {
+ return path.basename(filePath, getFileExtension(filePath))
+}
+
+function getConvertFileName(filePath) {
+ const name = getFileName(filePath)
+ return `${appPaths().convertPath}/${name}.png`
+}
+
+/**
+ * Check if converted image exists
+ * @returns {boolean}
+ */
+function checkFileExists(imagePath) {
+ const fileExists = fs.existsSync(imagePath)
+ return fileExists
+}
+
+function checkIfFile(filePath) {
+ return fs.statSync(filePath).isFile()
+}
+
+function diffFiles(fileOne, fileTwo) {
+ const f1 = fs.readFileSync(fileOne)
+ const f2 = fs.readFileSync(fileTwo)
+ return f1.equals(f2)
+}
+
+function dateModified(file) {
+ return fs.statSync(file).mtime
+}
+
+function dateDiff(file) {
+ const now = new Date().getTime()
+ const then = dateModified(file).getTime()
+ return Math.round(Math.abs((then - now) / 86400000))
+}
+
+// Main script
+fs.readdirSync(appPaths().iconPath).forEach((fileName) => {
+ const image = {
+ id: fileName,
+ path: `${appPaths().iconPath}/${fileName}`,
+ convertPath: `${appPaths().convertPath}/${fileName}`,
+ async convertAndCopy() {
+ await convertFile(this.path, {
+ height: 32,
+ width: 32,
+ outputFilePath: this.convertPath,
+ }).then((outputFile) => {
+ console.log(`SVG Converted: ${outputFile}`)
+ })
+ },
+ processFile() {
+ // Setup variables.
+ const ext = getFileExtension(this.path)
+
+ // If SVG, run checks.
+ if (ext === '.svg') {
+ // Check if converted file exists.
+ const convertFilePath = getConvertFileName(this.path)
+ if (checkFileExists(convertFilePath)) {
+ // If file has changed in past 7 days.
+ if (dateDiff(this.path) > 8) {
+ console.log(`File exists, skipping: ${this.id}`)
+ return null
+ }
+ }
+ // Convert and copy file.
+ this.convertAndCopy()
+ } else {
+ // If PNG or other, just copy the file as-is.
+ // eslint-disable-next-line no-lonely-if
+ if (checkIfFile(this.path)) {
+ copyFiles(this.path, this.convertPath)
+ } else {
+ console.info('Not a file, skipping...')
+ }
+ }
+ },
+ }
+
+ image.processFile()
+})
+
+/**
+
+cd ; cp *.svg converted ; cd converted ; convert-svg-to-png *.svg --width 32 --height 32 ; rm *.svg
+(async() => {
+ const inputFilePath = '/path/to/my-image.svg';
+ const outputFilePath = await convertFile(inputFilePath);
+
+ console.log(outputFilePath);
+ //=> "/path/to/my-image.png"
+})();
+*/
diff --git a/bin/validate.js b/bin/validate.js
index 2b9274734..7c9c9b238 100755
--- a/bin/validate.js
+++ b/bin/validate.js
@@ -6,171 +6,167 @@ const { technologies, categories } = JSON.parse(
fs.readFileSync('./src/technologies.json')
)
-try {
- Object.keys(technologies).forEach((name) => {
- const technology = technologies[name]
-
- // Validate regular expressions
- ;['url', 'html', 'meta', 'headers', 'cookies', 'script', 'js'].forEach(
- (type) => {
- if (technology[type]) {
- const keyed =
- typeof technology[type] === 'string' ||
- Array.isArray(technology[type])
- ? { _: technology[type] }
- : technology[type]
-
- Object.keys(keyed).forEach((key) => {
- const patterns = Array.isArray(keyed[key])
- ? keyed[key]
- : [keyed[key]]
-
- patterns.forEach((pattern, index) => {
- const id = `${name}: ${type}[${key === '_' ? `${index}` : key}]`
-
- const [regex, ...flags] = pattern.split('\\;')
-
- let maxGroups = 0
-
- flags.forEach((flag) => {
- const [key, value] = flag.split(':')
-
- if (key === 'version') {
- const refs = value.match(/\\(\d+)/g)
-
- if (refs) {
- maxGroups = refs.reduce((max, ref) =>
- Math.max(max, parseInt(refs[1] || 0))
- )
- }
- } else if (key === 'confidence') {
- if (
- !/^\d+$/.test(value) ||
- parseInt(value, 10) < 0 ||
- parseInt(value, 10) > 99
- ) {
- throw new Error(
- `Confidence value must a number between 0 and 99: ${value} (${id})`
- )
- }
- } else {
- throw new Error(`Invalid flag: ${key} (${id})`)
- }
- })
-
- // Validate regular expression
- try {
- // eslint-disable-next-line no-new
- new RegExp(regex)
- } catch (error) {
- throw new Error(`${error.message} (${id})`)
- }
-
- // Count capture groups
- const groups = new RegExp(`${regex}|`).exec('').length - 1
+Object.keys(technologies).forEach((name) => {
+ const technology = technologies[name]
- if (groups > maxGroups) {
- throw new Error(
- `Too many non-capturing groups, expected ${maxGroups}: ${regex} (${id})`
- )
- }
+ // Validate regular expressions
+ ;['url', 'html', 'meta', 'headers', 'cookies', 'script', 'js'].forEach(
+ (type) => {
+ if (technology[type]) {
+ const keyed =
+ typeof technology[type] === 'string' ||
+ Array.isArray(technology[type])
+ ? { _: technology[type] }
+ : technology[type]
- if (type === 'html' && !/[<>]/.test(regex)) {
- throw new Error(
- `HTML pattern must include < or >: ${regex} (${id})`
- )
- }
- })
- })
- }
- }
- )
+ Object.keys(keyed).forEach((key) => {
+ const patterns = Array.isArray(keyed[key]) ? keyed[key] : [keyed[key]]
- // Validate categories
- technology.cats.forEach((id) => {
- if (!categories[id]) {
- throw new Error(`No such category: ${id} (${name})`)
- }
- })
+ patterns.forEach((pattern, index) => {
+ const id = `${name}: ${type}[${key === '_' ? `${index}` : key}]`
- // Validate icons
- if (technology.icon && !fs.existsSync(`${iconPath}/${technology.icon}`)) {
- throw new Error(`No such icon: ${technology.icon} (${name})`)
- }
+ const [regex, ...flags] = pattern.split('\\;')
- // Validate website URLs
- try {
- // eslint-disable-next-line no-new
- const { protocol } = new URL(technology.website)
+ let maxGroups = 0
- if (protocol !== 'http:' && protocol !== 'https:') {
- throw new Error('Invalid protocol')
- }
- } catch (error) {
- throw new Error(`Invalid website URL: ${technology.website} (${name})`)
- }
+ flags.forEach((flag) => {
+ const [key, value] = flag.split(':')
- // Validate implies and excludes
- const { implies, excludes } = technology
+ if (key === 'version') {
+ const refs = value.match(/\\(\d+)/g)
- if (implies) {
- ;(Array.isArray(implies) ? implies : [implies]).forEach((implied) => {
- const [_name, ...flags] = implied.split('\\;')
+ if (refs) {
+ maxGroups = refs.reduce((max, ref) =>
+ Math.max(max, parseInt(refs[1] || 0))
+ )
+ }
+ } else if (key === 'confidence') {
+ if (
+ !/^\d+$/.test(value) ||
+ parseInt(value, 10) < 0 ||
+ parseInt(value, 10) > 99
+ ) {
+ throw new Error(
+ `Confidence value must a number between 0 and 99: ${value} (${id})`
+ )
+ }
+ } else {
+ throw new Error(`Invalid flag: ${key} (${id})`)
+ }
+ })
- const id = `${name}: implies[${implied}]`
+ // Validate regular expression
+ try {
+ // eslint-disable-next-line no-new
+ new RegExp(regex)
+ } catch (error) {
+ throw new Error(`${error.message} (${id})`)
+ }
- if (!technologies[_name]) {
- throw new Error(`Implied technology does not exist: ${_name} (${id})`)
- }
+ // Count capture groups
+ const groups = new RegExp(`${regex}|`).exec('').length - 1
- flags.forEach((flag) => {
- const [key, value] = flag.split(':')
+ if (groups > maxGroups) {
+ throw new Error(
+ `Too many non-capturing groups, expected ${maxGroups}: ${regex} (${id})`
+ )
+ }
- if (key === 'confidence') {
- if (
- !/^\d+$/.test(value) ||
- parseInt(value, 10) < 0 ||
- parseInt(value, 10) > 99
- ) {
+ if (type === 'html' && !/[<>]/.test(regex)) {
throw new Error(
- `Confidence value must a number between 0 and 99: ${value} (${id})`
+ `HTML pattern must include < or >: ${regex} (${id})`
)
}
- } else {
- throw new Error(`Invalid flag: ${key} (${id})`)
- }
+ })
})
- })
+ }
}
+ )
- if (excludes) {
- ;(Array.isArray(excludes) ? excludes : [excludes]).forEach((excluded) => {
- const id = `${name}: excludes[${excluded}]`
-
- if (!technologies[excluded]) {
- throw new Error(
- `Excluded technology does not exist: ${excluded} (${id})`
- )
- }
- })
+ // Validate categories
+ technology.cats.forEach((id) => {
+ if (!categories[id]) {
+ throw new Error(`No such category: ${id} (${name})`)
}
})
// Validate icons
- fs.readdirSync(iconPath).forEach((file) => {
- const filePath = `${iconPath}/${file}`
+ if (technology.icon && !fs.existsSync(`${iconPath}/${technology.icon}`)) {
+ throw new Error(`No such icon: ${technology.icon} (${name})`)
+ }
+
+ // Validate website URLs
+ try {
+ // eslint-disable-next-line no-new
+ const { protocol } = new URL(technology.website)
+
+ if (protocol !== 'http:' && protocol !== 'https:') {
+ throw new Error('Invalid protocol')
+ }
+ } catch (error) {
+ throw new Error(`Invalid website URL: ${technology.website} (${name})`)
+ }
+
+ // Validate implies and excludes
+ const { implies, excludes } = technology
+
+ if (implies) {
+ ;(Array.isArray(implies) ? implies : [implies]).forEach((implied) => {
+ const [_name, ...flags] = implied.split('\\;')
+
+ const id = `${name}: implies[${implied}]`
- if (fs.statSync(filePath).isFile() && !file.startsWith('.')) {
- if (!/^(png|svg)$/i.test(file.split('.').pop())) {
- throw new Error(`Incorrect file type, expected PNG or SVG: ${filePath}`)
+ if (!technologies[_name]) {
+ throw new Error(`Implied technology does not exist: ${_name} (${id})`)
}
- if (!Object.values(technologies).some(({ icon }) => icon === file)) {
- throw new Error(`Extraneous file: ${filePath}}`)
+ flags.forEach((flag) => {
+ const [key, value] = flag.split(':')
+
+ if (key === 'confidence') {
+ if (
+ !/^\d+$/.test(value) ||
+ parseInt(value, 10) < 0 ||
+ parseInt(value, 10) > 99
+ ) {
+ throw new Error(
+ `Confidence value must a number between 0 and 99: ${value} (${id})`
+ )
+ }
+ } else {
+ throw new Error(`Invalid flag: ${key} (${id})`)
+ }
+ })
+ })
+ }
+
+ if (excludes) {
+ ;(Array.isArray(excludes) ? excludes : [excludes]).forEach((excluded) => {
+ const id = `${name}: excludes[${excluded}]`
+
+ if (!technologies[excluded]) {
+ throw new Error(
+ `Excluded technology does not exist: ${excluded} (${id})`
+ )
}
+ })
+ }
+})
+
+// Validate icons
+fs.readdirSync(iconPath).forEach((file) => {
+ const filePath = `${iconPath}/${file}`
+
+ if (fs.statSync(filePath).isFile() && !file.startsWith('.')) {
+ if (!/^(png|svg)$/i.test(file.split('.').pop())) {
+ throw new Error(`Incorrect file type, expected PNG or SVG: ${filePath}`)
}
- })
-} catch (error) {
- // eslint-disable-next-line no-console
- console.error(error.message)
-}
+
+ if (
+ !Object.values(technologies).some(({ icon }) => icon === file) &&
+ file !== 'default.svg'
+ ) {
+ throw new Error(`Extraneous file: ${filePath}}`)
+ }
+ }
+})
diff --git a/package.json b/package.json
index c24bd1c32..b9b66f450 100644
--- a/package.json
+++ b/package.json
@@ -8,17 +8,19 @@
"@nuxtjs/eslint-config": "^3.1.0",
"@nuxtjs/eslint-module": "^2.0.0",
"babel-eslint": "^10.1.0",
- "eslint": "^7.7.0",
- "eslint-config-prettier": "^6.11.0",
+ "eslint": "^7.13.0",
+ "eslint-config-prettier": "^6.15.0",
+ "eslint-plugin-json": "^2.1.2",
"eslint-plugin-nuxt": "^1.0.0",
"eslint-plugin-prettier": "^3.1.4",
- "prettier": "^2.1.0"
+ "prettier": "^2.1.2"
},
"scripts": {
"link": "node ./bin/link.js",
- "lint": "eslint --fix src/**/*.js",
+ "lint": "eslint src/**/*.{js,json}",
+ "lint:fix": "eslint --fix src/**/*.{js,json}",
"validate": "yarn run lint && jsonlint -qV ./schema.json ./src/technologies.json && node ./bin/validate.js",
- "convert": "cd ./src/drivers/webextension/images/icons ; cp *.svg converted ; cd converted ; convert-svg-to-png *.svg --width 32 --height 32 ; rm *.svg",
+ "convert": "node ./bin/convert.js",
"prettify": "jsonlint -si --trim-trailing-commas --enforce-double-quotes ./src/technologies.json",
"build": "yarn run link && yarn run validate && yarn run prettify && yarn run convert && node ./bin/build.js",
"build:safari": "xcrun safari-web-extension-converter --swift --project-location build --force src/drivers/webextension"
diff --git a/schema.json b/schema.json
index 21ad68335..ad056b728 100644
--- a/schema.json
+++ b/schema.json
@@ -41,6 +41,19 @@
"type": "string",
"pattern": "^.{0,500}$"
},
+ "oss": {
+ "type": "boolean"
+ },
+ "saas": {
+ "type": "boolean"
+ },
+ "pricing": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "pattern": "^(low|mid|high|freemium|poa|payg|onetime|recurring)$"
+ }
+ },
"cats": {
"type": "array",
"items": {
@@ -73,6 +86,22 @@
}
}
},
+ "dom": {
+ "type": "object",
+ "additionalProperties": false,
+ "patternProperties": {
+ "^.+$": {
+ }
+ }
+ },
+ "dns": {
+ "type": "object",
+ "additionalProperties": false,
+ "patternProperties": {
+ "^.+$": {
+ }
+ }
+ },
"headers": {
"type": "object",
"additionalProperties": false,
@@ -194,6 +223,9 @@
},
"icon": {
"$ref": "#/definitions/non-empty-non-blank-string"
+ },
+ "xhr": {
+ "$ref": "#/definitions/non-empty-non-blank-string"
}
}
}
diff --git a/src/drivers/npm/README.md b/src/drivers/npm/README.md
index 9bba60c30..7c1d950e6 100644
--- a/src/drivers/npm/README.md
+++ b/src/drivers/npm/README.md
@@ -66,9 +66,9 @@ const options = {
htmlMaxRows: 2000,
};
-;(async function() {
- const wappalyzer = await new Wappalyzer(options)
+const wappalyzer = new Wappalyzer(options)
+;(async function() {
try {
await wappalyzer.init()
@@ -98,9 +98,9 @@ const Wappalyzer = require('wappalyzer');
const urls = ['https://www.wappalyzer.com', 'https://www.example.com']
-;(async function() {
- const wappalyzer = await new Wappalyzer()
+const wappalyzer = new Wappalyzer()
+;(async function() {
try {
await wappalyzer.init()
diff --git a/src/drivers/npm/cli.js b/src/drivers/npm/cli.js
index 1cab50af9..9e2644bdb 100755
--- a/src/drivers/npm/cli.js
+++ b/src/drivers/npm/cli.js
@@ -77,7 +77,7 @@ Options:
}
;(async function () {
- const wappalyzer = await new Wappalyzer(options)
+ const wappalyzer = new Wappalyzer(options)
try {
await wappalyzer.init()
diff --git a/src/drivers/npm/driver.js b/src/drivers/npm/driver.js
index a68696437..9404e727d 100644
--- a/src/drivers/npm/driver.js
+++ b/src/drivers/npm/driver.js
@@ -1,5 +1,6 @@
const { URL } = require('url')
const fs = require('fs')
+const dns = require('dns').promises
const path = require('path')
const http = require('http')
const https = require('https')
@@ -50,6 +51,8 @@ const { technologies, categories } = JSON.parse(
setTechnologies(technologies)
setCategories(categories)
+const xhrDebounce = []
+
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms))
}
@@ -67,12 +70,41 @@ function analyzeJs(js) {
)
}
+function analyzeDom(dom) {
+ return Array.prototype.concat.apply(
+ [],
+ dom.map(({ name, selector, text, property, attribute, value }) => {
+ const technology = Wappalyzer.technologies.find(
+ ({ name: _name }) => name === _name
+ )
+
+ if (text) {
+ return analyzeManyToMany(technology, 'dom.text', { [selector]: [text] })
+ }
+
+ if (property) {
+ return analyzeManyToMany(technology, `dom.properties.${property}`, {
+ [selector]: [value],
+ })
+ }
+
+ if (attribute) {
+ return analyzeManyToMany(technology, `dom.attributes.${attribute}`, {
+ [selector]: [value],
+ })
+ }
+
+ return []
+ })
+ )
+}
+
function get(url) {
if (['http:', 'https:'].includes(url.protocol)) {
const { get } = url.protocol === 'http:' ? http : https
return new Promise((resolve, reject) =>
- get(url.href, (response) => {
+ get(url, { rejectUnauthorized: false }, (response) => {
if (response.statusCode >= 400) {
return reject(
new Error(`${response.statusCode} ${response.statusMessage}`)
@@ -86,7 +118,7 @@ function get(url) {
response.on('data', (data) => (body += data))
response.on('error', (error) => reject(new Error(error.message)))
response.on('end', () => resolve(body))
- })
+ }).on('error', (error) => reject(new Error(error.message)))
)
} else {
throw new Error(`Invalid protocol: ${url.protocol}`)
@@ -127,6 +159,8 @@ class Driver {
try {
this.browser = await puppeteer.launch({
+ ignoreHTTPSErrors: true,
+ acceptInsecureCerts: true,
args: chromiumArgs,
executablePath: await chromiumBin,
})
@@ -135,7 +169,11 @@ class Driver {
this.log('Browser disconnected')
if (!this.destroyed) {
- await this.init()
+ try {
+ await this.init()
+ } catch (error) {
+ this.log(error.toString())
+ }
}
})
} catch (error) {
@@ -173,7 +211,11 @@ class Driver {
class Site {
constructor(url, headers = {}, driver) {
- ;({ options: this.options, browser: this.browser } = driver)
+ ;({
+ options: this.options,
+ browser: this.browser,
+ init: this.initDriver,
+ } = driver)
this.options.headers = {
...this.options.headers,
@@ -194,6 +236,8 @@ class Site {
this.listeners = {}
this.pages = []
+
+ this.dns = []
}
log(message, source = 'driver', type = 'log') {
@@ -225,12 +269,26 @@ class Site {
}
}
- timeout() {
- return new Promise((resolve, reject) =>
- setTimeout(() => {
- reject(new Error('The website took too long to respond'))
- }, this.options.maxWait)
- )
+ promiseTimeout(
+ promise,
+ errorMessage = 'The website took too long to respond'
+ ) {
+ let timeout = null
+
+ return Promise.race([
+ new Promise((resolve, reject) => {
+ timeout = setTimeout(() => {
+ clearTimeout(timeout)
+
+ reject(new Error(errorMessage))
+ }, this.options.maxWait)
+ }),
+ promise.then((value) => {
+ clearTimeout(timeout)
+
+ return value
+ }),
+ ])
}
async goto(url) {
@@ -249,7 +307,11 @@ class Site {
}
if (!this.browser) {
- throw new Error('Browser closed')
+ await this.initDriver()
+
+ if (!this.browser) {
+ throw new Error('Browser closed')
+ }
}
const page = await this.browser.newPage()
@@ -268,6 +330,26 @@ class Site {
page.on('request', async (request) => {
try {
+ if (request.resourceType() === 'xhr') {
+ let hostname
+
+ try {
+ ;({ hostname } = new URL(request.url()))
+ } catch (error) {
+ return
+ }
+
+ if (!xhrDebounce.includes(hostname)) {
+ xhrDebounce.push(hostname)
+
+ setTimeout(() => {
+ xhrDebounce.splice(xhrDebounce.indexOf(hostname), 1)
+
+ this.onDetect(analyze({ xhr: hostname }))
+ }, 1000)
+ }
+ }
+
if (
(responseReceived && request.isNavigationRequest()) ||
request.frame() !== page.mainFrame() ||
@@ -308,8 +390,6 @@ class Site {
]
})
- this.contentType = headers['content-type'] || null
-
if (response.status() >= 300 && response.status() < 400) {
if (headers.location) {
url = new URL(headers.location.slice(-1), url)
@@ -323,7 +403,7 @@ class Site {
this.onDetect(analyze({ headers, certIssuer }))
- await this.emit('response', { page, response })
+ await this.emit('response', { page, response, headers, certIssuer })
}
}
} catch (error) {
@@ -337,99 +417,103 @@ class Site {
)
try {
- await Promise.race([
- this.timeout(),
- page.goto(url.href, { waitUntil: 'domcontentloaded' }),
- ])
+ await this.promiseTimeout(
+ page.goto(url.href, { waitUntil: 'domcontentloaded' })
+ )
await sleep(1000)
+ // page.on('console', (message) => this.log(message.text()))
+
// Links
- const links = await (
- await Promise.race([
- this.timeout(),
- page.evaluateHandle(() =>
- Array.from(document.getElementsByTagName('a')).map(
- ({ hash, hostname, href, pathname, protocol, rel }) => ({
- hash,
- hostname,
- href,
- pathname,
- protocol,
- rel,
- })
+ const links = await this.promiseTimeout(
+ (
+ await this.promiseTimeout(
+ page.evaluateHandle(() =>
+ Array.from(document.getElementsByTagName('a')).map(
+ ({ hash, hostname, href, pathname, protocol, rel }) => ({
+ hash,
+ hostname,
+ href,
+ pathname,
+ protocol,
+ rel,
+ })
+ )
)
- ),
- ])
- ).jsonValue()
+ )
+ ).jsonValue()
+ )
// CSS
- const css = await (
- await Promise.race([
- this.timeout(),
- page.evaluateHandle((maxRows) => {
- const css = []
-
- try {
- if (!document.styleSheets.length) {
- return ''
- }
+ const css = await this.promiseTimeout(
+ (
+ await this.promiseTimeout(
+ page.evaluateHandle((maxRows) => {
+ const css = []
+
+ try {
+ if (!document.styleSheets.length) {
+ return ''
+ }
- for (const sheet of Array.from(document.styleSheets)) {
- for (const rules of Array.from(sheet.cssRules)) {
- css.push(rules.cssText)
+ for (const sheet of Array.from(document.styleSheets)) {
+ for (const rules of Array.from(sheet.cssRules)) {
+ css.push(rules.cssText)
- if (css.length >= maxRows) {
- break
+ if (css.length >= maxRows) {
+ break
+ }
}
}
+ } catch (error) {
+ return ''
}
- } catch (error) {
- return ''
- }
- return css.join('\n')
- }, this.options.htmlMaxRows),
- ])
- ).jsonValue()
+ return css.join('\n')
+ }, this.options.htmlMaxRows)
+ )
+ ).jsonValue()
+ )
// Script tags
- const scripts = await (
- await Promise.race([
- this.timeout(),
- page.evaluateHandle(() =>
- Array.from(document.getElementsByTagName('script'))
- .map(({ src }) => src)
- .filter((src) => src)
- ),
- ])
- ).jsonValue()
+ const scripts = await this.promiseTimeout(
+ (
+ await this.promiseTimeout(
+ page.evaluateHandle(() =>
+ Array.from(document.getElementsByTagName('script'))
+ .map(({ src }) => src)
+ .filter((src) => src)
+ )
+ )
+ ).jsonValue()
+ )
// Meta tags
- const meta = await (
- await Promise.race([
- this.timeout(),
- page.evaluateHandle(() =>
- Array.from(document.querySelectorAll('meta')).reduce(
- (metas, meta) => {
- const key =
- meta.getAttribute('name') || meta.getAttribute('property')
-
- if (key) {
- metas[key.toLowerCase()] = [meta.getAttribute('content')]
- }
+ const meta = await this.promiseTimeout(
+ (
+ await this.promiseTimeout(
+ page.evaluateHandle(() =>
+ Array.from(document.querySelectorAll('meta')).reduce(
+ (metas, meta) => {
+ const key =
+ meta.getAttribute('name') || meta.getAttribute('property')
+
+ if (key) {
+ metas[key.toLowerCase()] = [meta.getAttribute('content')]
+ }
- return metas
- },
- {}
+ return metas
+ },
+ {}
+ )
)
- ),
- ])
- ).jsonValue()
+ )
+ ).jsonValue()
+ )
// JavaScript
- const js = await Promise.race([
- this.timeout(),
+ const js = await this.promiseTimeout(
page.evaluate(
(technologies) => {
return technologies.reduce((technologies, { name, chains }) => {
@@ -461,14 +545,91 @@ class Site {
Wappalyzer.technologies
.filter(({ js }) => Object.keys(js).length)
.map(({ name, js }) => ({ name, chains: Object.keys(js) }))
- ),
- ])
+ )
+ )
+
+ // DOM
+ const dom = await this.promiseTimeout(
+ page.evaluate(
+ (technologies) => {
+ return technologies.reduce((technologies, { name, dom }) => {
+ const toScalar = (value) =>
+ typeof value === 'string' || typeof value === 'number'
+ ? value
+ : !!value
+
+ Object.keys(dom).forEach((selector) => {
+ const nodes = document.querySelectorAll(selector)
+
+ if (!nodes.length) {
+ return
+ }
+
+ dom[selector].forEach(({ text, properties, attributes }) => {
+ nodes.forEach((node) => {
+ if (text) {
+ const value = node.textContent.trim()
+
+ if (value) {
+ technologies.push({
+ name,
+ selector,
+ text: value,
+ })
+ }
+ }
+
+ if (properties) {
+ Object.keys(properties).forEach((property) => {
+ if (
+ Object.prototype.hasOwnProperty.call(node, property)
+ ) {
+ const value = node[property]
+
+ if (typeof value !== 'undefined') {
+ technologies.push({
+ name,
+ selector,
+ property,
+ value: toScalar(value),
+ })
+ }
+ }
+ })
+ }
+
+ if (attributes) {
+ Object.keys(attributes).forEach((attribute) => {
+ if (node.hasAttribute(attribute)) {
+ const value = node.getAttribute(attribute)
+
+ technologies.push({
+ name,
+ selector,
+ attribute,
+ value: toScalar(value),
+ })
+ }
+ })
+ }
+ })
+ })
+ })
+
+ return technologies
+ }, [])
+ },
+ Wappalyzer.technologies
+ .filter(({ dom }) => dom)
+ .map(({ name, dom }) => ({ name, dom }))
+ )
+ )
// Cookies
const cookies = (await page.cookies()).reduce(
(cookies, { name, value }) => ({
...cookies,
- [name]: [value],
+ [name.toLowerCase()]: [value],
}),
{}
)
@@ -497,8 +658,63 @@ class Site {
html = batches.join('\n')
}
+ // DNS
+ if (!Object.keys(this.dns).length) {
+ const records = {}
+ const resolve = (func, hostname) => {
+ return this.promiseTimeout(
+ func(hostname).catch((error) => {
+ if (error.code !== 'ENODATA') {
+ this.error(error)
+ }
+
+ return []
+ })
+ )
+ }
+
+ const domain = url.hostname.replace(/^www\./, '')
+
+ ;[
+ records.cname,
+ records.ns,
+ records.mx,
+ records.txt,
+ records.soa,
+ ] = await Promise.all([
+ resolve(dns.resolveCname, url.hostname),
+ resolve(dns.resolveNs, domain),
+ resolve(dns.resolveMx, domain),
+ resolve(dns.resolveTxt, domain),
+ resolve(dns.resolveSoa, domain),
+ ])
+
+ this.dns = Object.keys(records).reduce((dns, type) => {
+ dns[type] = dns[type] || []
+
+ Array.prototype.push.apply(
+ dns[type],
+ Array.isArray(records[type])
+ ? records[type].map((value) => {
+ return typeof value === 'object'
+ ? Object.values(value).join(' ')
+ : value
+ })
+ : [Object.values(records[type]).join(' ')]
+ )
+
+ return dns
+ }, {})
+
+ this.onDetect(analyze({ dns: this.dns }))
+ }
+
// Validate response
- if (url.protocol !== 'file:' && !this.analyzedUrls[url.href].status) {
+ if (
+ url.protocol !== 'file:' &&
+ this.analyzedUrls[url.href] &&
+ !this.analyzedUrls[url.href].status
+ ) {
await page.close()
this.log('Page closed')
@@ -506,8 +722,8 @@ class Site {
throw new Error('No response from server')
}
+ this.onDetect(analyzeDom(dom))
this.onDetect(analyzeJs(js))
-
this.onDetect(
analyze({
url,
@@ -530,9 +746,8 @@ class Site {
) &&
link.protocol &&
link.protocol.match(/https?:/) &&
- link.rel !== 'nofollow' &&
link.hostname === url.hostname &&
- extensions.test(link.pathname)
+ extensions.test(link.pathname.slice(-5))
) {
results.push(new URL(link.href.split('#')[0]))
}
@@ -551,6 +766,7 @@ class Site {
meta,
js,
links: reducedLinks,
+ dns: this.dns,
})
await page.close()
@@ -559,7 +775,15 @@ class Site {
return reducedLinks
} catch (error) {
- this.error(error)
+ if (error.constructor.name === 'TimeoutError') {
+ throw new Error('The website took too long to respond')
+ }
+
+ if (error.message.includes('net::ERR_NAME_NOT_RESOLVED')) {
+ throw new Error('Hostname could not be resolved')
+ }
+
+ throw new Error(error.message)
}
}
diff --git a/src/drivers/npm/package.json b/src/drivers/npm/package.json
index 59fb46b9d..19b45e17d 100644
--- a/src/drivers/npm/package.json
+++ b/src/drivers/npm/package.json
@@ -13,7 +13,7 @@
"software"
],
"homepage": "https://www.wappalyzer.com/",
- "version": "6.3.2",
+ "version": "6.5.27",
"author": "Wappalyzer",
"license": "MIT",
"repository": {
@@ -25,7 +25,7 @@
"url": "https://github.com/sponsors/aliasio"
},
{
- "url": "https://paypal.me/aliasio"
+ "url": "https://paypal.me/elbertalias"
}
],
"main": "driver.js",
@@ -40,6 +40,6 @@
"wappalyzer": "./cli.js"
},
"dependencies": {
- "puppeteer": "^5.2.1"
+ "puppeteer": "^5.3.0"
}
-}
+}
\ No newline at end of file
diff --git a/src/drivers/npm/yarn.lock b/src/drivers/npm/yarn.lock
index 96f84a2a0..1ebd145e4 100644
--- a/src/drivers/npm/yarn.lock
+++ b/src/drivers/npm/yarn.lock
@@ -76,10 +76,10 @@ debug@4, debug@^4.1.0, debug@^4.1.1:
dependencies:
ms "^2.1.1"
-devtools-protocol@0.0.781568:
- version "0.0.781568"
- resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.781568.tgz#4cdca90a952d2c77831096ff6cd32695d8715a04"
- integrity sha512-9Uqnzy6m6zEStluH9iyJ3iHyaQziFnMnLeC8vK0eN6smiJmIx7+yB64d67C2lH/LZra+5cGscJAJsNXO+MdPMg==
+devtools-protocol@0.0.799653:
+ version "0.0.799653"
+ resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.799653.tgz#86fc95ce5bf4fdf4b77a58047ba9d2301078f119"
+ integrity sha512-t1CcaZbvm8pOlikqrsIM9GOa7Ipp07+4h/q9u0JXBWjPCjHdBl9KkddX87Vv9vBHoBGtwV79sYQNGnQM6iS5gg==
end-of-stream@^1.1.0, end-of-stream@^1.4.1:
version "1.4.4"
@@ -264,13 +264,13 @@ pump@^3.0.0:
end-of-stream "^1.1.0"
once "^1.3.1"
-puppeteer@^5.2.1:
- version "5.2.1"
- resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-5.2.1.tgz#7f0564f0a5384f352a38c8cc42af875cd87f4ea6"
- integrity sha512-PZoZG7u+T6N1GFWBQmGVG162Ak5MAy8nYSVpeeQrwJK2oYUlDWpHEJPcd/zopyuEMTv7DiztS1blgny1txR2qw==
+puppeteer@^5.3.0:
+ version "5.3.0"
+ resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-5.3.0.tgz#0abf83d0f2d1273baf2b56885a813f8052903e33"
+ integrity sha512-GjqMk5GRro3TO0sw3QMsF1H7n+/jaK2OW45qMvqjYUyJ7y4oA//9auy969HHhTG3HZXaMxY/NWXF/NXlAFIvtw==
dependencies:
debug "^4.1.0"
- devtools-protocol "0.0.781568"
+ devtools-protocol "0.0.799653"
extract-zip "^2.0.0"
https-proxy-agent "^4.0.0"
mime "^2.0.3"
diff --git a/src/drivers/webextension/_locales/ca/messages.json b/src/drivers/webextension/_locales/ca/messages.json
index c5053ff24..6c155e8a4 100644
--- a/src/drivers/webextension/_locales/ca/messages.json
+++ b/src/drivers/webextension/_locales/ca/messages.json
@@ -1,24 +1,26 @@
{
- "github": { "message": "Fork Wappalyzer a GitHub!" },
+ "github": { "message": "Bifurcar Wappalyzer a GitHub" },
"twitter": { "message": "Seguir Wappalyzer a Twitter" },
"website": { "message": "Anar a wappalyzer.com" },
"options": { "message": "Opcions" },
"optionsSave": { "message": "Desar opcions" },
"optionsSaved": { "message": "Desat" },
- "optionUpgradeMessage": { "message": "Avisar-me quan hi hagi una actualització disponible" },
+ "optionUpgradeMessage": { "message": "Notificar les actualitzacions disponibles" },
"optionDynamicIcon": { "message": "Utilitzar la icona de la tecnologia enlloc del logotip de Wappalyzer" },
"optionTracking": { "message": "Enviar les tecnologies identificades de forma anònima a wappalyzer.com" },
- "optionThemeMode": { "message": "Habilitar la compatibilitat de la manera fosc." },
- "optionBadge": { "message": "Show the number of identified technologies on the icon" },
- "disableOnDomain": { "message": "Disable on this website" },
- "clearCache": { "message": "Clear cached detections" },
+ "optionThemeMode": { "message": "Habilitar la compatibilitat de l'aspecte fosc" },
+ "optionBadge": { "message": "Mostrar el nombre de tecnologies identificades en la icona" },
+ "disableOnDomain": { "message": "Desactivar en aquest web" },
+ "clearCache": { "message": "Esborrar la memòria cau de les deteccions" },
"nothingToDo": { "message": "Res a fer aquí." },
"noAppsDetected": { "message": "No s'ha detectat cap tecnologia." },
"categoryPin": { "message": "Mostrar sempre la icona" },
- "termsAccept": { "message": "Acceptar" },
+ "termsAccept": { "message": "M'està bé" },
+ "termsDecline": { "message": "Desactivar" },
"termsContent": { "message": "Aquesta extensió envia informació anònima sobre els llocs web que visiteu, inclosos el nom de domini i les tecnologies identificades a wappalyzer.com . Això pot desactivar-se a Opcions." },
"privacyPolicy": { "message": "Política de privadesa" },
- "createAlert": { "message": "Create an alert for this website" },
+ "createAlert": { "message": "Crear una alerta per aquest web" },
+ "leadLists": { "message": "Lead generation tools" },
"categoryName1": { "message": "CMS" },
"categoryName2": { "message": "Taulers de missatgeria" },
"categoryName3": { "message": "Gestor de bases de dades" },
@@ -35,7 +37,7 @@
"categoryName14": { "message": "Reproductors de vídeo" },
"categoryName15": { "message": "Sistemes de comentaris" },
"categoryName16": { "message": "Security" },
- "categoryName17": { "message": "Font Script" },
+ "categoryName17": { "message": "Tipografies" },
"categoryName18": { "message": "Marcs web" },
"categoryName19": { "message": "Miscel·lània" },
"categoryName20": { "message": "Editors" },
@@ -50,7 +52,7 @@
"categoryName29": { "message": "Motors de cerca" },
"categoryName30": { "message": "Correu web" },
"categoryName31": { "message": "CDN" },
- "categoryName32": { "message": "Marketing Automation" },
+ "categoryName32": { "message": "Automatitzacions de màrqueting" },
"categoryName33": { "message": "Extensions del servidor web" },
"categoryName34": { "message": "Bases de dades" },
"categoryName35": { "message": "Mapes" },
@@ -64,7 +66,7 @@
"categoryName43": { "message": "Paywall" },
"categoryName44": { "message": "Sistemes Build/CI" },
"categoryName45": { "message": "Sistemes SCADA" },
- "categoryName46": { "message": "Accés remot" },
+ "categoryName46": { "message": "Accessos remots" },
"categoryName47": { "message": "Eines de desenvolupament" },
"categoryName48": { "message": "Emmagatzematge de xarxa" },
"categoryName49": { "message": "Lectors de canals" },
@@ -76,7 +78,7 @@
"categoryName55": { "message": "Comptabilitat" },
"categoryName56": { "message": "Cryptominer" },
"categoryName57": { "message": "Generadors de llocs estàtics" },
- "categoryName58": { "message": "User Onboarding" },
+ "categoryName58": { "message": "Incorporacions d'usuaris" },
"categoryName59": { "message": "Llibreries JavaScript" },
"categoryName60": { "message": "Contenidors" },
"categoryName61": { "message": "SaaS" },
@@ -84,9 +86,14 @@
"categoryName63": { "message": "IaaS" },
"categoryName64": { "message": "Proxys invers" },
"categoryName65": { "message": "Balanceigs de càrrega" },
- "categoryName66": { "message": "UI Frameworks" },
+ "categoryName66": { "message": "Marcs UI" },
"categoryName67": { "message": "Cookie compliance" },
- "categoryName68": { "message": "Accessibility"},
- "categoryName69": { "message": "Social login"},
- "categoryName70": { "message": "SSL/TLS certificate authority"}
+ "categoryName68": { "message": "Accesibilitat" },
+ "categoryName69": { "message": "Inicis de sessió socials" },
+ "categoryName70": { "message": "Autoritats de certificació SSL/TLS" },
+ "categoryName71": { "message": "Programes d'afiliació" },
+ "categoryName72": { "message": "Programacions de cites" },
+ "categoryName73": { "message": "Enquestes" },
+ "categoryName74": { "message": "Testeigs A/B" },
+ "categoryName75": { "message": "Correus electrònics" }
}
diff --git a/src/drivers/webextension/_locales/de/messages.json b/src/drivers/webextension/_locales/de/messages.json
index 631d7010d..1ebb87c26 100644
--- a/src/drivers/webextension/_locales/de/messages.json
+++ b/src/drivers/webextension/_locales/de/messages.json
@@ -1,92 +1,99 @@
{
- "github": { "message": "Forke Wappalyzer bei GitHub!" },
- "twitter": { "message": "Folge Wappalyzer bei Twitter" },
- "website": { "message": "Gehe zu wappalyzer.com" },
- "options": { "message": "Optionen" },
- "optionsSave": { "message": "Optionen speichern" },
- "optionsSaved": { "message": "Gespeichert" },
- "optionUpgradeMessage": { "message": "Benachrichtige mich bei Upgrades" },
- "optionDynamicIcon": { "message": "Applikations Icon anstatt des Wappalyzer Icons verwenden" },
- "optionTracking": { "message": "Anonyme Statistiken an wappalyzer.com übermitteln" },
- "optionThemeMode": { "message": "Aktivieren dunklen Modus Kompatibilität." },
- "optionBadge": { "message": "Show the number of identified technologies on the icon" },
- "disableOnDomain": { "message": "Disable on this website" },
- "clearCache": { "message": "Clear cached detections" },
- "nothingToDo": { "message": "Nichts zu tun." },
- "noAppsDetected": { "message": "Keine Applikation entdeckt." },
- "categoryPin": { "message": "Immer Icon anzeigen" },
- "termsAccept": { "message": "Accept" },
- "termsContent": { "message": "This extension sends anonymous information about websites you visit, including domain name and identified technologies, to wappalyzer.com . This can be disabled in the settings." },
- "privacyPolicy": { "message": "Privacy policy" },
- "createAlert": { "message": "Create an alert for this website" },
- "categoryName1": { "message": "CMS" },
- "categoryName2": { "message": "Nachrichten Board" },
- "categoryName3": { "message": "Datenbankverwaltung" },
- "categoryName4": { "message": "Dokumentations Tool" },
- "categoryName5": { "message": "Widget" },
- "categoryName6": { "message": "Ecommerce" },
- "categoryName7": { "message": "Fotogalerien" },
- "categoryName8": { "message": "Wikis" },
- "categoryName9": { "message": "Hosting-Panels" },
- "categoryName10": { "message": "Statistiken" },
- "categoryName11": { "message": "Blog" },
- "categoryName12": { "message": "JavaScript Framework" },
- "categoryName13": { "message": "Fehlertracker" },
- "categoryName14": { "message": "Videospieler" },
- "categoryName15": { "message": "Kommentarsystem" },
- "categoryName16": { "message": "Security" },
- "categoryName17": { "message": "Schrift Script" },
- "categoryName18": { "message": "Web Framework" },
- "categoryName19": { "message": "Sonstiges" },
- "categoryName20": { "message": "Editor" },
- "categoryName21": { "message": "LMS" },
- "categoryName22": { "message": "Web Server" },
- "categoryName23": { "message": "Cache Tool" },
- "categoryName24": { "message": "Rich Text Editor" },
- "categoryName25": { "message": "JavaScript Graphics" },
- "categoryName26": { "message": "Mobile Framework" },
- "categoryName27": { "message": "Programmiersprache" },
- "categoryName28": { "message": "Betriebssystem" },
- "categoryName29": { "message": "Suchmaschine" },
- "categoryName30": { "message": "Webmail" },
- "categoryName31": { "message": "CDN" },
- "categoryName32": { "message": "Marketing Automation" },
- "categoryName33": { "message": "Web Server Erweiterung" },
- "categoryName34": { "message": "Datenbank" },
- "categoryName35": { "message": "Map" },
- "categoryName36": { "message": "Werbenetzwerk" },
- "categoryName37": { "message": "Netzwerkdienst" },
- "categoryName38": { "message": "Medienserver" },
- "categoryName39": { "message": "Webcam" },
- "categoryName40": { "message": "Drucker" },
- "categoryName41": { "message": "Zahlungsverarbeiter" },
- "categoryName42": { "message": "Schlagwort Manager" },
- "categoryName43": { "message": "Bezahlblockade" },
- "categoryName44": { "message": "Build/CI-System" },
- "categoryName45": { "message": "SCADA System" },
- "categoryName46": { "message": "Fernzugriff" },
- "categoryName47": { "message": "Entwicklungswerkzeug" },
- "categoryName48": { "message": "Netzwerkspeicher" },
- "categoryName49": { "message": "Feedleser" },
- "categoryName50": { "message": "Dokumentmanagementsysteme" },
- "categoryName51": { "message": "Startseitenersteller" },
- "categoryName52": { "message": "Live-Chat" },
- "categoryName53": { "message": "CRM" },
+ "github": { "message": "Forke Wappalyzer bei GitHub!" },
+ "twitter": { "message": "Folge Wappalyzer bei Twitter" },
+ "website": { "message": "Gehe zu wappalyzer.com" },
+ "options": { "message": "Optionen" },
+ "optionsSave": { "message": "Optionen speichern" },
+ "optionsSaved": { "message": "Gespeichert" },
+ "optionUpgradeMessage": { "message": "Benachrichtige mich bei Upgrades" },
+ "optionDynamicIcon": { "message": "Applikations Icon anstatt des Wappalyzer Icons verwenden" },
+ "optionTracking": { "message": "Anonyme Statistiken an wappalyzer.com übermitteln" },
+ "optionThemeMode": { "message": "Dunkel-Modus aktivieren" },
+ "optionBadge": { "message": "Anzahl der identifizierten Optionen am Icon anzeigen" },
+ "disableOnDomain": { "message": "Auf dieser Website deaktivieren" },
+ "clearCache": { "message": "Cache leeren" },
+ "nothingToDo": { "message": "Nichts zu tun." },
+ "noAppsDetected": { "message": "Keine Applikationen gefunden" },
+ "categoryPin": { "message": "Icon immer anzeigen" },
+ "termsAccept": { "message": "I'm ok with that" },
+ "termsDecline": { "message": "Disable" },
+ "termsContent": { "message": "Diese Erweiterung sendet anonyme Informationen über Websites, die Sie besuchen, einschließlich der Domain und der identifizierten Technologien, an wappalyzer.com . Dies kann in den Einstellungen deaktiviert werden." },
+ "privacyPolicy": { "message": "Datenschutzerklärung" },
+ "createAlert": { "message": "Alarm für diese Website erstellen" },
+ "leadLists": { "message": "Lead generation tools" },
+ "categoryName1": { "message": "CMS" },
+ "categoryName2": { "message": "Nachrichten Board" },
+ "categoryName3": { "message": "Datenbankverwaltung" },
+ "categoryName4": { "message": "Dokumentations Tool" },
+ "categoryName5": { "message": "Widget" },
+ "categoryName6": { "message": "E-Commerce" },
+ "categoryName7": { "message": "Fotogalerien" },
+ "categoryName8": { "message": "Wikis" },
+ "categoryName9": { "message": "Hosting-Panels" },
+ "categoryName10": { "message": "Statistiken" },
+ "categoryName11": { "message": "Blog" },
+ "categoryName12": { "message": "JavaScript Frameworks" },
+ "categoryName13": { "message": "Ticketsysteme" },
+ "categoryName14": { "message": "Videoplayer" },
+ "categoryName15": { "message": "Kommentarsystem" },
+ "categoryName16": { "message": "Security" },
+ "categoryName17": { "message": "Schrift Script" },
+ "categoryName18": { "message": "Web Frameworks" },
+ "categoryName19": { "message": "Sonstiges" },
+ "categoryName20": { "message": "Editor" },
+ "categoryName21": { "message": "LMS" },
+ "categoryName22": { "message": "Web Server" },
+ "categoryName23": { "message": "Cache Tool" },
+ "categoryName24": { "message": "Rich Text Editor" },
+ "categoryName25": { "message": "JavaScript Graphics" },
+ "categoryName26": { "message": "Mobile Framework" },
+ "categoryName27": { "message": "Programmiersprache" },
+ "categoryName28": { "message": "Betriebssysteme" },
+ "categoryName29": { "message": "Suchmaschinen" },
+ "categoryName30": { "message": "Webmail" },
+ "categoryName31": { "message": "CDN" },
+ "categoryName32": { "message": "Marketing Automation" },
+ "categoryName33": { "message": "Web Server Erweiterungen" },
+ "categoryName34": { "message": "Datenbanken" },
+ "categoryName35": { "message": "Karten" },
+ "categoryName36": { "message": "Werbenetzwerke" },
+ "categoryName37": { "message": "Netzwerkdienste" },
+ "categoryName38": { "message": "Medienserver" },
+ "categoryName39": { "message": "Web-Kameras" },
+ "categoryName40": { "message": "Drucker" },
+ "categoryName41": { "message": "Zahlungsverarbeiter" },
+ "categoryName42": { "message": "Tag Manager" },
+ "categoryName43": { "message": "Bezahlblockade" },
+ "categoryName44": { "message": "CI-Systeme" },
+ "categoryName45": { "message": "SCADA System" },
+ "categoryName46": { "message": "Fernzugriff" },
+ "categoryName47": { "message": "Entwicklungswerkzeuge" },
+ "categoryName48": { "message": "Netzwerkspeicher" },
+ "categoryName49": { "message": "Feedleser" },
+ "categoryName50": { "message": "Dokumentmanagementsysteme" },
+ "categoryName51": { "message": "Website Baukästen" },
+ "categoryName52": { "message": "Live-Chat" },
+ "categoryName53": { "message": "CRM" },
"categoryName54": { "message": "SEO" },
- "categoryName55": { "message": "Buchhaltung" },
- "categoryName56": { "message": "Cryptominer" },
- "categoryName57": { "message": "Statischer Seitengenerator" },
- "categoryName58": { "message": "Benutzer-Einbindung" },
- "categoryName59": { "message": "JavaScript Bibliotheken" },
- "categoryName60": { "message": "Containers" },
- "categoryName61": { "message": "SaaS" },
- "categoryName62": { "message": "PaaS" },
- "categoryName63": { "message": "IaaS" },
- "categoryName64": { "message": "Reverse Proxy" },
- "categoryName65": { "message": "Load Balancer" },
- "categoryName66": { "message": "UI Frameworks" },
- "categoryName67": { "message": "Cookie compliance" },
- "categoryName68": { "message": "Accessibility"},
- "categoryName69": { "message": "Social login"},
- "categoryName70": { "message": "SSL/TLS certificate authority"}
+ "categoryName55": { "message": "Buchhaltung" },
+ "categoryName56": { "message": "Cryptominer" },
+ "categoryName57": { "message": "Statischer Seitengenerator" },
+ "categoryName58": { "message": "Benutzer-Onboarding" },
+ "categoryName59": { "message": "JavaScript Bibliotheken" },
+ "categoryName60": { "message": "Container" },
+ "categoryName61": { "message": "SaaS" },
+ "categoryName62": { "message": "PaaS" },
+ "categoryName63": { "message": "IaaS" },
+ "categoryName64": { "message": "Reverse Proxies" },
+ "categoryName65": { "message": "Load Balancer" },
+ "categoryName66": { "message": "UI Frameworks" },
+ "categoryName67": { "message": "Cookie compliance" },
+ "categoryName68": { "message": "Barrierefreiheit" },
+ "categoryName69": { "message": "Social Login" },
+ "categoryName70": { "message": "SSL/TLS certificate authority" },
+ "categoryName71": { "message": "Partnerprogram" },
+ "categoryName72": { "message": "Appointment scheduling" },
+ "categoryName73": { "message": "Surveys" },
+ "categoryName74": { "message": "A/B testing" },
+ "categoryName75": { "message": "Email" }
}
diff --git a/src/drivers/webextension/_locales/el/messages.json b/src/drivers/webextension/_locales/el/messages.json
index d96216826..da4d31f66 100644
--- a/src/drivers/webextension/_locales/el/messages.json
+++ b/src/drivers/webextension/_locales/el/messages.json
@@ -15,10 +15,12 @@
"nothingToDo": { "message": "Καμία ενέργεια." },
"noAppsDetected": { "message": "Δεν ανιχνεύθηκαν εφαρμογές." },
"categoryPin": { "message": "Always show icon" },
- "termsAccept": { "message": "Accept" },
+ "termsAccept": { "message": "I'm ok with that" },
+ "termsDecline": { "message": "Disable" },
"termsContent": { "message": "This extension sends anonymous information about websites you visit, including domain name and identified technologies, to wappalyzer.com . This can be disabled in the settings." },
"privacyPolicy": { "message": "Privacy policy" },
"createAlert": { "message": "Create an alert for this website" },
+ "leadLists": { "message": "Lead generation tools" },
"categoryName1": { "message": "CMS" },
"categoryName2": { "message": "Διαδικτυακό Φόρουμ" },
"categoryName3": { "message": "Διαχειριστής Βάσης Δεδομένων" },
@@ -83,6 +85,11 @@
"categoryName66": { "message": "UI Frameworks" },
"categoryName67": { "message": "Cookie compliance" },
"categoryName68": { "message": "Accessibility" },
- "categoryName69": { "message": "Social login" },
- "categoryName70": { "message": "SSL/TLS certificate authority" }
+ "categoryName69": { "message": "Social logins" },
+ "categoryName70": { "message": "SSL/TLS certificate authorities" },
+ "categoryName71": { "message": "Affiliate programs" },
+ "categoryName72": { "message": "Appointment scheduling" },
+ "categoryName73": { "message": "Surveys" },
+ "categoryName74": { "message": "A/B testing" },
+ "categoryName75": { "message": "Email" }
}
diff --git a/src/drivers/webextension/_locales/en/messages.json b/src/drivers/webextension/_locales/en/messages.json
index 453b284bb..411afba68 100644
--- a/src/drivers/webextension/_locales/en/messages.json
+++ b/src/drivers/webextension/_locales/en/messages.json
@@ -15,10 +15,12 @@
"nothingToDo": { "message": "Nothing to do here." },
"noAppsDetected": { "message": "No technologies detected." },
"categoryPin": { "message": "Always show icon" },
- "termsAccept": { "message": "Accept" },
+ "termsAccept": { "message": "I'm ok with that" },
+ "termsDecline": { "message": "Disable" },
"termsContent": { "message": "This extension sends anonymous information about websites you visit, including domain name and identified technologies, to wappalyzer.com . This can be disabled in the settings." },
"privacyPolicy": { "message": "Privacy policy" },
"createAlert": { "message": "Create an alert for this website" },
+ "leadLists": { "message": "Lead generation tools" },
"categoryName1": { "message": "CMS" },
"categoryName2": { "message": "Message boards" },
"categoryName3": { "message": "Database managers" },
@@ -85,6 +87,12 @@
"categoryName66": { "message": "UI frameworks" },
"categoryName67": { "message": "Cookie compliance" },
"categoryName68": { "message": "Accessibility" },
- "categoryName69": { "message": "Social login" },
- "categoryName70": { "message": "SSL/TLS certificate authority" }
+ "categoryName69": { "message": "Social logins" },
+ "categoryName70": { "message": "SSL/TLS certificate authorities" },
+ "categoryName71": { "message": "Affiliate programs" },
+ "categoryName72": { "message": "Appointment scheduling" },
+ "categoryName73": { "message": "Surveys" },
+ "categoryName74": { "message": "A/B testing" },
+ "categoryName75": { "message": "Email" },
+ "categoryName76": { "message": "Personalization" }
}
diff --git a/src/drivers/webextension/_locales/es/messages.json b/src/drivers/webextension/_locales/es/messages.json
index b073fb5aa..8862e3e0c 100644
--- a/src/drivers/webextension/_locales/es/messages.json
+++ b/src/drivers/webextension/_locales/es/messages.json
@@ -15,10 +15,12 @@
"nothingToDo": { "message": "Nada que hacer aquí." },
"noAppsDetected": { "message": "Aplicaciones no detectadas." },
"categoryPin": { "message": "Always show icon" },
- "termsAccept": { "message": "Accept" },
+ "termsAccept": { "message": "I'm ok with that" },
+ "termsDecline": { "message": "Disable" },
"termsContent": { "message": "This extension sends anonymous information about websites you visit, including domain name and identified technologies, to wappalyzer.com . This can be disabled in the settings." },
"privacyPolicy": { "message": "Privacy policy" },
"createAlert": { "message": "Create an alert for this website" },
+ "leadLists": { "message": "Lead generation tools" },
"categoryName1": { "message": "Gestor de Contenido" },
"categoryName2": { "message": "Foro" },
"categoryName3": { "message": "Gestor de Bases de Datos" },
@@ -55,7 +57,7 @@
"categoryName34": { "message": "Base de Datos" },
"categoryName35": { "message": "Mapa" },
"categoryName36": { "message": "Red de Publicidad" },
- "categoryName37": { "message": "Network Sevice" },
+ "categoryName37": { "message": "Network Service" },
"categoryName38": { "message": "Media Server" },
"categoryName39": { "message": "Webcam" },
"categoryName40": { "message": "Printer" },
@@ -87,6 +89,11 @@
"categoryName66": { "message": "UI Frameworks" },
"categoryName67": { "message": "Cookie compliance" },
"categoryName68": { "message": "Accessibility" },
- "categoryName69": { "message": "Social login" },
- "categoryName70": { "message": "SSL/TLS certificate authority" }
+ "categoryName69": { "message": "Social logins" },
+ "categoryName70": { "message": "SSL/TLS certificate authorities" },
+ "categoryName71": { "message": "Affiliate programs" },
+ "categoryName72": { "message": "Appointment scheduling" },
+ "categoryName73": { "message": "Surveys" },
+ "categoryName74": { "message": "A/B testing" },
+ "categoryName75": { "message": "Email" }
}
diff --git a/src/drivers/webextension/_locales/fa/messages.json b/src/drivers/webextension/_locales/fa/messages.json
index 8a221fefb..b4db06e3b 100644
--- a/src/drivers/webextension/_locales/fa/messages.json
+++ b/src/drivers/webextension/_locales/fa/messages.json
@@ -10,15 +10,17 @@
"optionTracking": { "message": "ارسال فن آوری های شناسایی شده به صورت ناشناس به wappalyzer.com" },
"optionThemeMode": { "message": "فعال کردن حالت سازگاری تاریک." },
"nothingToDo": { "message": "هیچ چیز برای انجام اینجا نیست." },
- "optionBadge": { "message": "Show the number of identified technologies on the icon" },
- "disableOnDomain": { "message": "Disable on this website" },
- "clearCache": { "message": "Clear cached detections" },
+ "optionBadge": { "message": "نمایش تعداد فناوری های شناسایی شده روی آیکون" },
+ "disableOnDomain": { "message": "غیرفعال کردن در این وبسایت" },
+ "clearCache": { "message": "پاکسازی شناسایی های کش شده" },
"noAppsDetected": { "message": "هیچ فنآوری شناسایی نشده است." },
"categoryPin": { "message": "همیشه نماد را نشان بده" },
- "termsAccept": { "message": "قبول" },
+ "termsAccept": { "message": "I'm ok with that" },
+ "termsDecline": { "message": "Disable" },
"termsContent": { "message": "این افزونه اطلاعات وبسایتهای بازدید شده توسط شما را به صورت ناشناس ارسال میکند، مانند آدرس سایت و تکنولوژیهای استفاده شده در آن سایت را ارسال میکند. اطلاعات بیشتر در wappalyzer.com . شما میتوانید این افزونه را غیرفعال کنید." },
- "privacyPolicy": { "message": "Privacy policy" },
- "createAlert": { "message": "Create an alert for this website" },
+ "privacyPolicy": { "message": "سیاست حفظ حریم خصوصی" },
+ "createAlert": { "message": "ساخت یک هشدار برای این وبسایت" },
+ "leadLists": { "message": "Lead generation tools" },
"categoryName1": { "message": "سیستم مدیریت محتوا" },
"categoryName2": { "message": "انجمن پیام" },
"categoryName3": { "message": "مدیریت پایگاه داده" },
@@ -34,7 +36,7 @@
"categoryName13": { "message": "ردیاب مشکل" },
"categoryName14": { "message": "پخش کننده ویدیویی" },
"categoryName15": { "message": "سیستم نظرسنجی" },
- "categoryName16": { "message": "Security" },
+ "categoryName16": { "message": "امنیت" },
"categoryName17": { "message": "اسکریپ فونت" },
"categoryName18": { "message": "چارچوب وب" },
"categoryName19": { "message": "متفرقه" },
@@ -83,10 +85,15 @@
"categoryName62": { "message": "PaaS" },
"categoryName63": { "message": "IaaS" },
"categoryName64": { "message": "پروکسی معکوس" },
- "categoryName65": { "message": "Load Balancer" },
- "categoryName66": { "message": "UI Frameworks" },
+ "categoryName65": { "message": "لودبالانسر" },
+ "categoryName66": { "message": "فریمورکهای رابط کاربری" },
"categoryName67": { "message": "Cookie compliance" },
- "categoryName68": { "message": "Accessibility" },
- "categoryName69": { "message": "Social login" },
- "categoryName70": { "message": "SSL/TLS certificate authority" }
+ "categoryName68": { "message": "دسترسی" },
+ "categoryName69": { "message": "ورود به شبکه های اجتماعی" },
+ "categoryName70": { "message": "صادر کننده SSL/TLS" },
+ "categoryName71": { "message": "Affiliate programs" },
+ "categoryName72": { "message": "Appointment scheduling" },
+ "categoryName73": { "message": "Surveys" },
+ "categoryName74": { "message": "A/B testing" },
+ "categoryName75": { "message": "Email" }
}
diff --git a/src/drivers/webextension/_locales/fr/messages.json b/src/drivers/webextension/_locales/fr/messages.json
index 9f58d9b7f..80ce42fbd 100644
--- a/src/drivers/webextension/_locales/fr/messages.json
+++ b/src/drivers/webextension/_locales/fr/messages.json
@@ -1,5 +1,5 @@
{
- "github": { "message": "Forker Wappalyzer sur GitHub!" },
+ "github": { "message": "Forker Wappalyzer sur GitHub !" },
"noAppsDetected": { "message": "Pas d'applications détectées." },
"nothingToDo": { "message": "Rien à faire ici." },
"optionTracking": { "message": "Envoyer anonymement des rapports sur les applications détectées à wappalyzer.com pour la recherche" },
@@ -9,16 +9,18 @@
"options": { "message": "Options" },
"optionsSave": { "message": "Sauvegarder les options" },
"optionsSaved": { "message": "Sauvegardé" },
- "optionBadge": { "message": "Show the number of identified technologies on the icon" },
- "disableOnDomain": { "message": "Disable on this website" },
- "clearCache": { "message": "Clear cached detections" },
+ "optionBadge": { "message": "Montrer le nombre de technologies identifiées sur l'icône" },
+ "disableOnDomain": { "message": "Désactiver sur ce site web" },
+ "clearCache": { "message": "Effacer les détections mises en cache" },
"twitter": { "message": "Suivre Wappalyzer sur Twitter" },
"website": { "message": "Aller sur wappalyzer.com" },
"categoryPin": { "message": " Toujours afficher l'icône" },
- "termsAccept": { "message": "Accept" },
+ "termsAccept": { "message": "Je suis OK avec ça" },
+ "termsDecline": { "message": "Désactiver" },
"termsContent": { "message": "This extension sends anonymous information about websites you visit, including domain name and identified technologies, to wappalyzer.com . This can be disabled in the settings." },
- "privacyPolicy": { "message": "Privacy policy" },
- "createAlert": { "message": "Create an alert for this website" },
+ "privacyPolicy": { "message": "Politique de confidentialité" },
+ "createAlert": { "message": "Créer une alerte pour ce site web" },
+ "leadLists": { "message": "Lead generation tools" },
"categoryName1": { "message": "CMS" },
"categoryName2": { "message": "Forum" },
"categoryName3": { "message": "Gestionnaire de base de données" },
@@ -34,7 +36,7 @@
"categoryName13": { "message": "Outil de suivi de problèmes" },
"categoryName14": { "message": "Lecteur de vidéos" },
"categoryName15": { "message": "Système de commentaires" },
- "categoryName16": { "message": "Security" },
+ "categoryName16": { "message": "Sécurité" },
"categoryName17": { "message": "Script de police" },
"categoryName18": { "message": "Framework web" },
"categoryName19": { "message": "Divers" },
@@ -68,7 +70,7 @@
"categoryName47": { "message": "Outil de développement" },
"categoryName48": { "message": "Stockage réseau" },
"categoryName49": { "message": "Lecteur de flux RSS" },
- "categoryName51": { "message": "Créateur de Landing Page" },
+ "categoryName51": { "message": "Créateur de \"Landing Page\"" },
"categoryName50": { "message": "Système de gestion de documents" },
"categoryName52": { "message": "Chat en direct" },
"categoryName53": { "message": "CRM" },
@@ -77,16 +79,21 @@
"categoryName56": { "message": "Crypto-mineur" },
"categoryName57": { "message": "Générateur de site statique" },
"categoryName58": { "message": "User Onboarding" },
- "categoryName59": { "message": "JavaScript Libraries" },
- "categoryName60": { "message": "Containers" },
+ "categoryName59": { "message": "Librairies JavaScript" },
+ "categoryName60": { "message": "Conteneurs" },
"categoryName61": { "message": "SaaS" },
"categoryName62": { "message": "PaaS" },
"categoryName63": { "message": "IaaS" },
- "categoryName64": { "message": "Reverse Proxy" },
+ "categoryName64": { "message": "Proxy inversé" },
"categoryName65": { "message": "Load Balancer" },
- "categoryName66": { "message": "UI Frameworks" },
+ "categoryName66": { "message": "Frameworks UI" },
"categoryName67": { "message": "Cookie compliance" },
- "categoryName68": { "message": "Accessibility" },
- "categoryName69": { "message": "Social login" },
- "categoryName70": { "message": "SSL/TLS certificate authority" }
+ "categoryName68": { "message": "Accessibilité" },
+ "categoryName69": { "message": "Connexion avec les réseaux sociaux" },
+ "categoryName70": { "message": "SSL/TLS certificate authorities" },
+ "categoryName71": { "message": "Programmes affiliés" },
+ "categoryName72": { "message": "Appointment scheduling" },
+ "categoryName73": { "message": "Surveys" },
+ "categoryName74": { "message": "A/B testing" },
+ "categoryName75": { "message": "Email" }
}
diff --git a/src/drivers/webextension/_locales/gl_ES/messages.json b/src/drivers/webextension/_locales/gl_ES/messages.json
index fb8fa26fd..fd48da1b0 100644
--- a/src/drivers/webextension/_locales/gl_ES/messages.json
+++ b/src/drivers/webextension/_locales/gl_ES/messages.json
@@ -15,10 +15,12 @@
"nothingToDo": { "message": "Nada que facer por aquí." },
"noAppsDetected": { "message": "Non se identificaron aplicativos." },
"categoryPin": { "message": "Amosar sempre icono" },
- "termsAccept": { "message": "Aceptar" },
+ "termsAccept": { "message": "I'm ok with that" },
+ "termsDecline": { "message": "Disable" },
"termsContent": { "message": "Esta extensión envía anonimamente información acerca das webs que visitas, incluindo dominio e aplicativos identificados, a wappalyzer.com . Isto pode ser desactivado nas preferencias." },
"privacyPolicy": { "message": "Política de privacidade" },
"createAlert": { "message": "Create an alert for this website" },
+ "leadLists": { "message": "Lead generation tools" },
"categoryName1": { "message": "CMS" },
"categoryName2": { "message": "Taboleiro de mensaxes" },
"categoryName3": { "message": "Xestor de base de datos" },
@@ -87,6 +89,11 @@
"categoryName66": { "message": "UI Frameworks" },
"categoryName67": { "message": "Cookie compliance" },
"categoryName68": { "message": "Accessibility" },
- "categoryName69": { "message": "Social login" },
- "categoryName70": { "message": "SSL/TLS certificate authority" }
+ "categoryName69": { "message": "Social logins" },
+ "categoryName70": { "message": "SSL/TLS certificate authorities" },
+ "categoryName71": { "message": "Affiliate programs" },
+ "categoryName72": { "message": "Appointment scheduling" },
+ "categoryName73": { "message": "Surveys" },
+ "categoryName74": { "message": "A/B testing" },
+ "categoryName75": { "message": "Email" }
}
diff --git a/src/drivers/webextension/_locales/gr/messages.json b/src/drivers/webextension/_locales/gr/messages.json
index fcd10ad82..e46f38736 100644
--- a/src/drivers/webextension/_locales/gr/messages.json
+++ b/src/drivers/webextension/_locales/gr/messages.json
@@ -15,10 +15,12 @@
"nothingToDo": { "message": "Καμία ενέργεια." },
"noAppsDetected": { "message": "Δεν ανιχνεύθηκαν εφαρμογές." },
"categoryPin": { "message": "Always show icon" },
- "termsAccept": { "message": "Accept" },
+ "termsAccept": { "message": "I'm ok with that" },
+ "termsDecline": { "message": "Disable" },
"termsContent": { "message": "This extension sends anonymous information about websites you visit, including domain name and identified technologies, to wappalyzer.com . This can be disabled in the settings." },
"privacyPolicy": { "message": "Privacy policy" },
"createAlert": { "message": "Create an alert for this website" },
+ "leadLists": { "message": "Lead generation tools" },
"categoryName1": { "message": "CMS" },
"categoryName2": { "message": "Διαδικτυακό Φόρουμ" },
"categoryName3": { "message": "Διαχειριστής Βάσης Δεδομένων" },
@@ -83,6 +85,11 @@
"categoryName66": { "message": "UI Frameworks" },
"categoryName67": { "message": "Cookie compliance" },
"categoryName68": { "message": "Accessibility" },
- "categoryName69": { "message": "Social login" },
- "categoryName70": { "message": "SSL/TLS certificate authority" }
+ "categoryName69": { "message": "Social logins" },
+ "categoryName70": { "message": "SSL/TLS certificate authorities" },
+ "categoryName71": { "message": "Affiliate programs" },
+ "categoryName72": { "message": "Appointment scheduling" },
+ "categoryName73": { "message": "Surveys" },
+ "categoryName74": { "message": "A/B testing" },
+ "categoryName75": { "message": "Email" }
}
diff --git a/src/drivers/webextension/_locales/id/messages.json b/src/drivers/webextension/_locales/id/messages.json
index a0d1b2897..bee46f188 100644
--- a/src/drivers/webextension/_locales/id/messages.json
+++ b/src/drivers/webextension/_locales/id/messages.json
@@ -15,10 +15,12 @@
"nothingToDo": { "message": "Tak ada yang dilakukan disini." },
"noAppsDetected": { "message": "Tidak ada aplikasi yang terdeteksi." },
"categoryPin": { "message": "Always show icon" },
- "termsAccept": { "message": "Accept" },
+ "termsAccept": { "message": "I'm ok with that" },
+ "termsDecline": { "message": "Disable" },
"termsContent": { "message": "This extension sends anonymous information about websites you visit, including domain name and identified technologies, to wappalyzer.com . This can be disabled in the settings." },
"privacyPolicy": { "message": "Privacy policy" },
"createAlert": { "message": "Create an alert for this website" },
+ "leadLists": { "message": "Lead generation tools" },
"categoryName1": { "message": "Sistem Pengelola Konten" },
"categoryName2": { "message": "Papan Pesan" },
"categoryName3": { "message": "Pengelola Basis Data" },
@@ -87,6 +89,11 @@
"categoryName66": { "message": "UI Frameworks" },
"categoryName67": { "message": "Cookie compliance" },
"categoryName68": { "message": "Accessibility" },
- "categoryName69": { "message": "Social login" },
- "categoryName70": { "message": "SSL/TLS certificate authority" }
+ "categoryName69": { "message": "Social logins" },
+ "categoryName70": { "message": "SSL/TLS certificate authorities" },
+ "categoryName71": { "message": "Affiliate programs" },
+ "categoryName72": { "message": "Appointment scheduling" },
+ "categoryName73": { "message": "Surveys" },
+ "categoryName74": { "message": "A/B testing" },
+ "categoryName75": { "message": "Email" }
}
diff --git a/src/drivers/webextension/_locales/it/messages.json b/src/drivers/webextension/_locales/it/messages.json
index a80798521..2a066531b 100644
--- a/src/drivers/webextension/_locales/it/messages.json
+++ b/src/drivers/webextension/_locales/it/messages.json
@@ -15,10 +15,12 @@
"nothingToDo": { "message": "Niente da fare qui." },
"noAppsDetected": { "message": "Nessuna applicazione rilevata." },
"categoryPin": { "message": "Always show icon" },
- "termsAccept": { "message": "Accept" },
+ "termsAccept": { "message": "I'm ok with that" },
+ "termsDecline": { "message": "Disable" },
"termsContent": { "message": "This extension sends anonymous information about websites you visit, including domain name and identified technologies, to wappalyzer.com . This can be disabled in the settings." },
"privacyPolicy": { "message": "Privacy policy" },
"createAlert": { "message": "Create an alert for this website" },
+ "leadLists": { "message": "Lead generation tools" },
"categoryName1": { "message": "CMS" },
"categoryName2": { "message": "Forum" },
"categoryName3": { "message": "Gestore di Database" },
@@ -88,5 +90,10 @@
"categoryName67": { "message": "Cookie compliance" },
"categoryName68": { "message": "Accessibility" },
"categoryName69": { "message": "Social login" },
- "categoryName70": { "message": "SSL/TLS certificate authority" }
+ "categoryName70": { "message": "SSL/TLS certificate authority" },
+ "categoryName71": { "message": "Affiliate program" },
+ "categoryName72": { "message": "Appointment scheduling" },
+ "categoryName73": { "message": "Surveys" },
+ "categoryName74": { "message": "A/B testing" },
+ "categoryName75": { "message": "Email" }
}
diff --git a/src/drivers/webextension/_locales/ja/messages.json b/src/drivers/webextension/_locales/ja/messages.json
index 656901314..c74f77a5e 100644
--- a/src/drivers/webextension/_locales/ja/messages.json
+++ b/src/drivers/webextension/_locales/ja/messages.json
@@ -15,10 +15,12 @@
"nothingToDo": { "message": "ここでは特定出来ません。" },
"noAppsDetected": { "message": "技術は検出されませんでした。" },
"categoryPin": { "message": "常にアイコンを表示" },
- "termsAccept": { "message": "受諾する" },
+ "termsAccept": { "message": "I'm ok with that" },
+ "termsDecline": { "message": "Disable" },
"termsContent": { "message": "この拡張機能は、ドメイン名や特定された技術など、アクセスしたWebサイトに関する匿名情報をwappalyzer.com に送信します。これは設定で無効にできます。" },
"privacyPolicy": { "message": "プライバシーポリシー" },
"createAlert": { "message": "Create an alert for this website" },
+ "leadLists": { "message": "Lead generation tools" },
"categoryName1": { "message": "CMS" },
"categoryName2": { "message": "メッセージボード" },
"categoryName3": { "message": "データベースマネージャー" },
@@ -88,5 +90,10 @@
"categoryName67": { "message": "Cookie compliance" },
"categoryName68": { "message": "Accessibility" },
"categoryName69": { "message": "Social login" },
- "categoryName70": { "message": "SSL/TLS certificate authority" }
+ "categoryName70": { "message": "SSL/TLS certificate authority" },
+ "categoryName71": { "message": "Affiliate program" },
+ "categoryName72": { "message": "Appointment scheduling" },
+ "categoryName73": { "message": "Surveys" },
+ "categoryName74": { "message": "A/B testing" },
+ "categoryName75": { "message": "Email" }
}
diff --git a/src/drivers/webextension/_locales/ko/messages.json b/src/drivers/webextension/_locales/ko/messages.json
index 86a217896..d981343b6 100644
--- a/src/drivers/webextension/_locales/ko/messages.json
+++ b/src/drivers/webextension/_locales/ko/messages.json
@@ -15,10 +15,12 @@
"nothingToDo": { "message": "여기에는 할 일이 없네요." },
"noAppsDetected": { "message": "식별된 기술이 없습니다." },
"categoryPin": { "message": "항상 아이콘 보이기" },
- "termsAccept": { "message": "수락" },
+ "termsAccept": { "message": "I'm ok with that" },
+ "termsDecline": { "message": "Disable" },
"termsContent": { "message": "이 확장 기능은 사이트의 도메인과 식별된 기술을 포함한 익명 정보를 wappalyzer.com 에 전송합니다. 이 기능은 설정에서 비활성화 할 수 있습니다." },
"privacyPolicy": { "message": "개인정보처리방침" },
"createAlert": { "message": "이 웹 사이트에 대한 알림 받기" },
+ "leadLists": { "message": "Lead generation tools" },
"categoryName1": { "message": "CMS" },
"categoryName2": { "message": "포럼 소프트웨어" },
"categoryName3": { "message": "데이터베이스 관리 도구" },
@@ -86,5 +88,10 @@
"categoryName67": { "message": "쿠키 동의" },
"categoryName68": { "message": "접근성" },
"categoryName69": { "message": "소셜 로그인" },
- "categoryName70": { "message": "SSL/TLS certificate authority" }
+ "categoryName70": { "message": "SSL/TLS certificate authorities" },
+ "categoryName71": { "message": "Affiliate programs" },
+ "categoryName72": { "message": "Appointment scheduling" },
+ "categoryName73": { "message": "Surveys" },
+ "categoryName74": { "message": "A/B testing" },
+ "categoryName75": { "message": "Email" }
}
diff --git a/src/drivers/webextension/_locales/pl/messages.json b/src/drivers/webextension/_locales/pl/messages.json
index d000e321f..98c81a5c7 100644
--- a/src/drivers/webextension/_locales/pl/messages.json
+++ b/src/drivers/webextension/_locales/pl/messages.json
@@ -15,10 +15,12 @@
"nothingToDo": { "message": "Nic tu nie ma." },
"noAppsDetected": { "message": "Nie wykryto żadnych aplikacji." },
"categoryPin": { "message": "Zawsze pokazuj tą ikonę" },
- "termsAccept": { "message": "Akceptuj" },
+ "termsAccept": { "message": "I'm ok with that" },
+ "termsDecline": { "message": "Disable" },
"termsContent": { "message": "To rozszerzenie wysyła anonimowe informacje o stronach, które odwiedzasz, uwzględniając nazwy domen i zidentyfikowane technologie do wappalyzer.com . Opcja może zostać wyłączona w ustawieniach." },
"privacyPolicy": { "message": "Privacy policy" },
"createAlert": { "message": "Create an alert for this website" },
+ "leadLists": { "message": "Lead generation tools" },
"categoryName1": { "message": "System zarządzania treścią" },
"categoryName2": { "message": "Forum" },
"categoryName3": { "message": "Menedżer baz danych" },
@@ -87,6 +89,11 @@
"categoryName66": { "message": "UI Frameworks" },
"categoryName67": { "message": "Cookie compliance" },
"categoryName68": { "message": "Accessibility" },
- "categoryName69": { "message": "Social login" },
- "categoryName70": { "message": "SSL/TLS certificate authority" }
+ "categoryName69": { "message": "Social logins" },
+ "categoryName70": { "message": "SSL/TLS certificate authorities" },
+ "categoryName71": { "message": "Affiliate programs" },
+ "categoryName72": { "message": "Appointment scheduling" },
+ "categoryName73": { "message": "Surveys" },
+ "categoryName74": { "message": "A/B testing" },
+ "categoryName75": { "message": "Email" }
}
diff --git a/src/drivers/webextension/_locales/pt/messages.json b/src/drivers/webextension/_locales/pt/messages.json
index c4c41277d..38569e6ea 100644
--- a/src/drivers/webextension/_locales/pt/messages.json
+++ b/src/drivers/webextension/_locales/pt/messages.json
@@ -15,10 +15,12 @@
"twitter": { "message": "Seguir Wappalyzer no Twitter" },
"website": { "message": "Ir para wappalyzer.com" },
"categoryPin": { "message": "Mostrar sempre ícone" },
- "termsAccept": { "message": "Aceitar" },
+ "termsAccept": { "message": "I'm ok with that" },
+ "termsDecline": { "message": "Disable" },
"termsContent": { "message": "Esta extensão envia informações anónimas sobre os sites que visitas, incluindo o nome de domínio e as tecnologias identificadas, para o wappalyzer.com . Isso pode ser desativado nas configurações." },
"privacyPolicy": { "message": "Políticas de Privacidade" },
"createAlert": { "message": "Create an alert for this website" },
+ "leadLists": { "message": "Lead generation tools" },
"categoryName1": { "message": "CMS" },
"categoryName2": { "message": "Fórum" },
"categoryName3": { "message": "Gestor de Base de Dados" },
@@ -87,6 +89,11 @@
"categoryName66": { "message": "UI Frameworks" },
"categoryName67": { "message": "Cookie compliance" },
"categoryName68": { "message": "Accessibility" },
- "categoryName69": { "message": "Social login" },
- "categoryName70": { "message": "SSL/TLS certificate authority" }
+ "categoryName69": { "message": "Social logins" },
+ "categoryName70": { "message": "SSL/TLS certificate authorities" },
+ "categoryName71": { "message": "Affiliate programs" },
+ "categoryName72": { "message": "Appointment scheduling" },
+ "categoryName73": { "message": "Surveys" },
+ "categoryName74": { "message": "A/B testing" },
+ "categoryName75": { "message": "Email" }
}
diff --git a/src/drivers/webextension/_locales/pt_BR/messages.json b/src/drivers/webextension/_locales/pt_BR/messages.json
index 36f90a93f..5d36f4b55 100644
--- a/src/drivers/webextension/_locales/pt_BR/messages.json
+++ b/src/drivers/webextension/_locales/pt_BR/messages.json
@@ -15,10 +15,12 @@
"nothingToDo": { "message": "Nada a fazer aqui." },
"noAppsDetected": { "message": "Nenhuma tecnologia identificada." },
"categoryPin": { "message": "Sempre mostrar ícone" },
- "termsAccept": { "message": "Aceitar" },
+ "termsAccept": { "message": "I'm ok with that" },
+ "termsDecline": { "message": "Disable" },
"termsContent": { "message": "Esta extensão envia informações anônimas sobre os sites que você visita, incluindo domínio e tecnologias identificadas para wappalyzer.com . Este comportamento pode ser desativado nas configurações." },
"privacyPolicy": { "message": "Privacy policy" },
"createAlert": { "message": "Create an alert for this website" },
+ "leadLists": { "message": "Lead generation tools" },
"categoryName1": { "message": "CMS" },
"categoryName2": { "message": "Fórum" },
"categoryName3": { "message": "Gestão de Banco de Dados" },
@@ -87,6 +89,11 @@
"categoryName66": { "message": "UI Frameworks" },
"categoryName67": { "message": "Cookie compliance" },
"categoryName68": { "message": "Accessibility" },
- "categoryName69": { "message": "Social login" },
- "categoryName70": { "message": "SSL/TLS certificate authority" }
+ "categoryName69": { "message": "Social logins" },
+ "categoryName70": { "message": "SSL/TLS certificate authorities" },
+ "categoryName71": { "message": "Affiliate programs" },
+ "categoryName72": { "message": "Appointment scheduling" },
+ "categoryName73": { "message": "Surveys" },
+ "categoryName74": { "message": "A/B testing" },
+ "categoryName75": { "message": "Email" }
}
diff --git a/src/drivers/webextension/_locales/ro/messages.json b/src/drivers/webextension/_locales/ro/messages.json
index fb621d40c..7bea53eee 100644
--- a/src/drivers/webextension/_locales/ro/messages.json
+++ b/src/drivers/webextension/_locales/ro/messages.json
@@ -15,10 +15,12 @@
"nothingToDo": { "message": "Nimic de făcut pe pagina curentă." },
"noAppsDetected": { "message": "Nici o aplicație detectată." },
"categoryPin": { "message": "Afișează icon tot timpul" },
- "termsAccept": { "message": "Accept" },
+ "termsAccept": { "message": "I'm ok with that" },
+ "termsDecline": { "message": "Disable" },
"termsContent": { "message": "This extension sends anonymous information about websites you visit, including domain name and identified technologies, to wappalyzer.com . This can be disabled in the settings." },
"privacyPolicy": { "message": "Privacy policy" },
"createAlert": { "message": "Create an alert for this website" },
+ "leadLists": { "message": "Lead generation tools" },
"categoryName1": { "message": "CMS" },
"categoryName2": { "message": "Forum de discuții" },
"categoryName3": { "message": "Manager baze de date" },
@@ -83,6 +85,11 @@
"categoryName66": { "message": "UI Frameworks" },
"categoryName67": { "message": "Cookie compliance" },
"categoryName68": { "message": "Accessibility" },
- "categoryName69": { "message": "Social login" },
- "categoryName70": { "message": "SSL/TLS certificate authority" }
+ "categoryName69": { "message": "Social logins" },
+ "categoryName70": { "message": "SSL/TLS certificate authorities" },
+ "categoryName71": { "message": "Affiliate programs" },
+ "categoryName72": { "message": "Appointment scheduling" },
+ "categoryName73": { "message": "Surveys" },
+ "categoryName74": { "message": "A/B testing" },
+ "categoryName75": { "message": "Email" }
}
diff --git a/src/drivers/webextension/_locales/ru/messages.json b/src/drivers/webextension/_locales/ru/messages.json
index e1fd536ee..f4eff0f2a 100644
--- a/src/drivers/webextension/_locales/ru/messages.json
+++ b/src/drivers/webextension/_locales/ru/messages.json
@@ -15,10 +15,12 @@
"nothingToDo": { "message": "Здесь нечего делать" },
"noAppsDetected": { "message": "Не удалось определить ни одну технологию" },
"categoryPin": { "message": "Всегда отображать эту категорию иконкой"},
- "termsAccept": { "message": "Принять" },
+ "termsAccept": { "message": "I'm ok with that" },
+ "termsDecline": { "message": "Disable" },
"termsContent": { "message": "Расширение отправляет обезличенную статистику посещенных сайтов, включая доменное имя и распознанные технологии на wappalyzer.com . Это можно отключить в настройках." },
"privacyPolicy": { "message": "Политика конфиденциальности" },
"createAlert": { "message": "Отправить жалобу на этот сайт" },
+ "leadLists": { "message": "Lead generation tools" },
"categoryName1": { "message": "CMS" },
"categoryName2": { "message": "Форум" },
"categoryName3": { "message": "Менеджер БД" },
@@ -85,6 +87,12 @@
"categoryName66": { "message": "UI Фреймворк" },
"categoryName67": { "message": "Соответствие cookie" },
"categoryName68": { "message": "Доступность" },
- "categoryName69": { "message": "Social login" },
- "categoryName70": { "message": "SSL/TLS certificate authority" }
+ "categoryName69": { "message": "Логин через социальные сети" },
+ "categoryName70": { "message": "SSL/TLS certificate authorities" },
+ "categoryName71": { "message": "Партнерская программы" },
+ "categoryName72": { "message": "Сервисы расписания и бронирования" },
+ "categoryName73": { "message": "Опросы" },
+ "categoryName74": { "message": "A/B тестирование" },
+ "categoryName75": { "message": "Email" },
+ "categoryName76": { "message": "Персонализация" }
}
diff --git a/src/drivers/webextension/_locales/sk/messages.json b/src/drivers/webextension/_locales/sk/messages.json
index f5095fa72..d65f4b933 100644
--- a/src/drivers/webextension/_locales/sk/messages.json
+++ b/src/drivers/webextension/_locales/sk/messages.json
@@ -15,10 +15,12 @@
"nothingToDo": { "message": "Nie je tu čo robiť." },
"noAppsDetected": { "message": "Žiadne aplikácie neboli zistené." },
"categoryPin": { "message": "Always show icon" },
- "termsAccept": { "message": "Accept" },
+ "termsAccept": { "message": "I'm ok with that" },
+ "termsDecline": { "message": "Disable" },
"termsContent": { "message": "This extension sends anonymous information about websites you visit, including domain name and identified technologies, to wappalyzer.com . This can be disabled in the settings." },
"privacyPolicy": { "message": "Privacy policy" },
"createAlert": { "message": "Create an alert for this website" },
+ "leadLists": { "message": "Lead generation tools" },
"categoryName1": { "message": "CMS" },
"categoryName2": { "message": "Message Board" },
"categoryName3": { "message": "Správca databáz" },
@@ -87,6 +89,11 @@
"categoryName66": { "message": "UI Frameworks" },
"categoryName67": { "message": "Cookie compliance" },
"categoryName68": { "message": "Accessibility" },
- "categoryName69": { "message": "Social login" },
- "categoryName70": { "message": "SSL/TLS certificate authority" }
+ "categoryName69": { "message": "Social logins" },
+ "categoryName70": { "message": "SSL/TLS certificate authorities" },
+ "categoryName71": { "message": "Affiliate programs" },
+ "categoryName72": { "message": "Appointment scheduling" },
+ "categoryName73": { "message": "Surveys" },
+ "categoryName74": { "message": "A/B testing" },
+ "categoryName75": { "message": "Email" }
}
diff --git a/src/drivers/webextension/_locales/tr/messages.json b/src/drivers/webextension/_locales/tr/messages.json
index 832a58bc3..a69f197f9 100644
--- a/src/drivers/webextension/_locales/tr/messages.json
+++ b/src/drivers/webextension/_locales/tr/messages.json
@@ -15,10 +15,12 @@
"nothingToDo": { "message": "Burada yapacak birşey yok." },
"noAppsDetected": { "message": "Uygulamalar tespit edilemedi." },
"categoryPin": { "message": "Her zaman bu kategorinin ikonunu kullan" },
- "termsAccept": { "message": "Kabul Ediyorum" },
+ "termsAccept": { "message": "I'm ok with that" },
+ "termsDecline": { "message": "Disable" },
"termsContent": { "message": "Bu eklenti, ziyaret ettiğiniz web site bilgilerini, alan adları ve tespit edilen teknolojiler ile beraber anonim olarak wappalyzer.com 'a gönderir. Bunu, eklenti ayarlarından değiştirebilirsiniz." },
"privacyPolicy": { "message": "Privacy policy" },
"createAlert": { "message": "Create an alert for this website" },
+ "leadLists": { "message": "Lead generation tools" },
"categoryName1": { "message": "İçerik Yönetim Sistemi" },
"categoryName2": { "message": "Mesaj Tahtası" },
"categoryName3": { "message": "Veritabanı Yöneticisi" },
@@ -87,6 +89,11 @@
"categoryName66": { "message": "UI Frameworks" },
"categoryName67": { "message": "Cookie compliance" },
"categoryName68": { "message": "Accessibility" },
- "categoryName69": { "message": "Social login" },
- "categoryName70": { "message": "SSL/TLS certificate authority" }
+ "categoryName69": { "message": "Social logins" },
+ "categoryName70": { "message": "SSL/TLS certificate authorities" },
+ "categoryName71": { "message": "Affiliate programs"},
+ "categoryName72": { "message": "Appointment scheduling" },
+ "categoryName73": { "message": "Surveys" },
+ "categoryName74": { "message": "A/B testing" },
+ "categoryName75": { "message": "Email" }
}
diff --git a/src/drivers/webextension/_locales/uk/messages.json b/src/drivers/webextension/_locales/uk/messages.json
index a6f7d5daa..29dc12412 100644
--- a/src/drivers/webextension/_locales/uk/messages.json
+++ b/src/drivers/webextension/_locales/uk/messages.json
@@ -15,10 +15,12 @@
"nothingToDo": { "message": "Тут нічого робити." },
"noAppsDetected": { "message": "Нічого не знайдено." },
"categoryPin": { "message": "Завжди показувати іконку Wappalyzer" },
- "termsAccept": { "message": "Доступ" },
+ "termsAccept": { "message": "I'm ok with that" },
+ "termsDecline": { "message": "Disable" },
"termsContent": { "message": "Це розширення надсилає на Wapplayzer.com анонімну інформацію про відвідувані вами веб-сайти, включаючи доменні імена та визначені технології. Це можна відключити в налаштуваннях." },
"privacyPolicy": { "message": "Політика приватності" },
"createAlert": { "message": "Поскаржитись на цей сайт" },
+ "leadLists": { "message": "Lead generation tools" },
"categoryName1": { "message": "CMS" },
"categoryName2": { "message": "Форум" },
"categoryName3": { "message": "Менеджер БД" },
@@ -87,6 +89,11 @@
"categoryName66": { "message": "UI Каркаси" },
"categoryName67": { "message": "Відповідність файлам cookie" },
"categoryName68": { "message": "Доступність" },
- "categoryName69": { "message": "Social login" },
- "categoryName70": { "message": "SSL/TLS certificate authority" }
+ "categoryName69": { "message": "Social logins" },
+ "categoryName70": { "message": "SSL/TLS certificate authorities" },
+ "categoryName71": { "message": "Affiliate programs" },
+ "categoryName72": { "message": "Appointment scheduling" },
+ "categoryName73": { "message": "Surveys" },
+ "categoryName74": { "message": "A/B testing" },
+ "categoryName75": { "message": "Email" }
}
diff --git a/src/drivers/webextension/_locales/uz/messages.json b/src/drivers/webextension/_locales/uz/messages.json
index af62b2567..7f1989fa2 100644
--- a/src/drivers/webextension/_locales/uz/messages.json
+++ b/src/drivers/webextension/_locales/uz/messages.json
@@ -15,10 +15,12 @@
"nothingToDo": { "message": "Bu yerda tekshirib bolmaydi." },
"noAppsDetected": { "message": "Hech qanday dastur aniqlanmadi." },
"categoryPin": { "message": "Always show icon" },
- "termsAccept": { "message": "Accept" },
+ "termsAccept": { "message": "I'm ok with that" },
+ "termsDecline": { "message": "Disable" },
"termsContent": { "message": "This extension sends anonymous information about websites you visit, including domain name and identified technologies, to wappalyzer.com . This can be disabled in the settings." },
"privacyPolicy": { "message": "Privacy policy" },
"createAlert": { "message": "Create an alert for this website" },
+ "leadLists": { "message": "Lead generation tools" },
"categoryName1": { "message": "CMS (KBT)" },
"categoryName2": { "message": "Forum" },
"categoryName3": { "message": "MB boshqaruvi" },
@@ -86,7 +88,12 @@
"categoryName65": { "message": "Load Balancer" },
"categoryName66": { "message": "UI Frameworks" },
"categoryName67": { "message": "Cookie compliance" },
- "categoryName68": { "message": "Accessibility"},
- "categoryName69": { "message": "Social login"},
- "categoryName70": { "message": "SSL/TLS certificate authority" }
+ "categoryName68": { "message": "Accessibility" },
+ "categoryName69": { "message": "Social logins" },
+ "categoryName70": { "message": "SSL/TLS certificate authorities" },
+ "categoryName71": { "message": "Affiliate programs" },
+ "categoryName72": { "message": "Appointment scheduling" },
+ "categoryName73": { "message": "Surveys" },
+ "categoryName74": { "message": "A/B testing" },
+ "categoryName75": { "message": "Email" }
}
diff --git a/src/drivers/webextension/_locales/zh_CN/messages.json b/src/drivers/webextension/_locales/zh_CN/messages.json
index 8a37b95ca..280d0bdf3 100644
--- a/src/drivers/webextension/_locales/zh_CN/messages.json
+++ b/src/drivers/webextension/_locales/zh_CN/messages.json
@@ -15,10 +15,12 @@
"nothingToDo": { "message": "这里无事可做。" },
"noAppsDetected": { "message": "未检测到任何技术。" },
"categoryPin": { "message": "总是显示图标" },
- "termsAccept": { "message": "接受" },
+ "termsAccept": { "message": "I'm ok with that" },
+ "termsDecline": { "message": "Disable" },
"termsContent": { "message": "此扩展程序会匿名发送您访问的网站信息至 wappalyzer.com ,包含域名和检测到的技术。这可以在设置中禁用。" },
"privacyPolicy": { "message": "隐私政策" },
"createAlert": { "message": "为该网站创建提醒" },
+ "leadLists": { "message": "Lead generation tools" },
"categoryName1": { "message": "内容管理系统(CMS)" },
"categoryName2": { "message": "信息板" },
"categoryName3": { "message": "数据库管理器" },
@@ -84,7 +86,10 @@
"categoryName65": { "message": "负载均衡" },
"categoryName66": { "message": "用户界面(UI)框架" },
"categoryName67": { "message": "Cookie 合规" },
- "categoryName68": { "message": "辅助功能"},
- "categoryName69": { "message": "社交登录"},
- "categoryName70": { "message": "SSL/TLS certificate authority" }
+ "categoryName68": { "message": "辅助功能" },
+ "categoryName69": { "message": "社交登录" },
+ "categoryName70": { "message": "SSL/TLS certificate authority" },
+ "categoryName71": { "message": "Affiliate program" },
+ "categoryName74": { "message": "A/B testing" },
+ "categoryName75": { "message": "Email" }
}
diff --git a/src/drivers/webextension/_locales/zh_TW/messages.json b/src/drivers/webextension/_locales/zh_TW/messages.json
index 575a241f0..3a1950994 100644
--- a/src/drivers/webextension/_locales/zh_TW/messages.json
+++ b/src/drivers/webextension/_locales/zh_TW/messages.json
@@ -15,10 +15,12 @@
"nothingToDo": { "message": "這裡什麼也沒有。" },
"noAppsDetected": { "message": "未識別到技術。" },
"categoryPin": { "message": "永遠顯示圖示" },
- "termsAccept": { "message": "接受" },
+ "termsAccept": { "message": "I'm ok with that" },
+ "termsDecline": { "message": "Disable" },
"termsContent": { "message": "這個擴充功能將你所造訪網站的網域名稱和識別到的技術等資訊,匿名傳送至 wappalyzer.com 。你可以在選項中停用。" },
"privacyPolicy": { "message": "Privacy policy" },
"createAlert": { "message": "Create an alert for this website" },
+ "leadLists": { "message": "Lead generation tools" },
"categoryName1": { "message": "內容管理系統(CMS)" },
"categoryName2": { "message": "留言板/討論區" },
"categoryName3": { "message": "資料庫管理" },
@@ -88,5 +90,10 @@
"categoryName67": { "message": "Cookie compliance" },
"categoryName68": { "message": "Accessibility" },
"categoryName69": { "message": "Social login" },
- "categoryName70": { "message": "SSL/TLS certificate authority" }
+ "categoryName70": { "message": "SSL/TLS certificate authorities" },
+ "categoryName71": { "message": "Affiliate programs" },
+ "categoryName72": { "message": "Appointment scheduling" },
+ "categoryName73": { "message": "Surveys" },
+ "categoryName74": { "message": "A/B testing" },
+ "categoryName75": { "message": "Email" }
}
diff --git a/src/drivers/webextension/css/styles.css b/src/drivers/webextension/css/styles.css
index 45bd52ab3..7546e8946 100644
--- a/src/drivers/webextension/css/styles.css
+++ b/src/drivers/webextension/css/styles.css
@@ -44,22 +44,51 @@ a:hover {
border-bottom: 1px solid var(--color-secondary);
display: flex;
height: 4.5rem;
+ justify-content: space-between;
+ padding: 0 1.5rem;
}
.header__logo {
height: 2.5rem;
- margin: .5rem 1.5rem 0 1.5rem;
+ margin-top: .5rem;
}
.header__logo--dark {
display: none;
}
+.header__icon {
+ color: var(--color-primary);
+ cursor: pointer;
+ height: 1.1rem;
+ margin-left: 1rem;
+ vertical-align: middle;
+ width: 1.1rem;
+}
+
+.header__switch {
+ height: 1.5rem;
+ width: 1.5rem;
+}
+
+.header__switch--hidden {
+ display: none;
+}
+
+.header__switch--disabled {
+ color: var(--color-text);
+}
+
+.spacer {
+ flex-grow:1;
+}
+
.footer {
align-items: center;
background: #fff;
bottom: 0;
border-top: 1px solid var(--color-secondary);
+ font-size: .8rem;
height: 3rem;
display: flex;
padding: 0 1.5rem;
@@ -67,46 +96,26 @@ a:hover {
width: 100%;
}
-.alerts {
+.footer__links {
white-space: nowrap;
}
-.alerts--hidden {
- visibility: hidden;
+.footer__link {
+ display: inline-block;
+ padding: 0 .3rem;
}
-.alerts__icon {
- color: var(--color-primary);
- height: 1.1rem;
- margin-right: .5rem;
- vertical-align: text-bottom;
- width: 1.1rem;
+.footer__link:first-child {
+ padding-left: 0;
}
-.spacer {
- flex-grow:1;
-}
-
-.footer__icon {
- color: var(--color-primary);
- cursor: pointer;
- height: 1.1rem;
- margin-left: 1rem;
- vertical-align: middle;
- width: 1.1rem;
-}
-
-.footer__switch {
- height: 1.5rem;
- width: 1.5rem;
-}
-
-.footer__switch--hidden {
- display: none;
+.footer__link a {
+ border-bottom: 1px solid var(--color-secondary);
+ text-decoration: none;
}
-.footer__switch--disabled {
- color: var(--color-text);
+.footer__link a:hover {
+ border-bottom: 1px solid var(--color-primary);
}
.detections {
@@ -195,6 +204,21 @@ a:hover {
.technology__link {
color: var(--color-text);
+ display: block;
+ width: 100%;
+}
+
+.technology__link .technology__name {
+ border-bottom: 1px solid var(--color-secondary);
+}
+
+.technology__link:hover {
+ text-decoration: none;
+}
+
+.technology__link:hover .technology__name {
+ border-bottom: 1px solid var(--color-primary);
+ color: var(--color-primary);
}
.technology__confidence {
@@ -208,7 +232,7 @@ a:hover {
border-radius: 3px;
font-size: .7rem;
padding: .1rem .3rem;
- margin-left: .4rem;
+ margin-left: .2rem;
vertical-align: middle;
}
@@ -218,7 +242,7 @@ a:hover {
flex-direction: column;
justify-content: center;
padding: 1.5rem 1.5rem 1rem 1.5rem;
- height: 12rem;
+ height: 14rem;
width: 36rem;
}
@@ -244,18 +268,25 @@ a:hover {
width: 80%;
}
-.terms__accept {
+.terms__buttons {
+}
+
+.terms__button {
background-color: #4608ad;
border: none;
border-radius: 3px;
color: white;
cursor: pointer;
font-size: .9rem;
- padding: .8rem 3rem;
+ padding: 0 2rem;
+ margin: 0 .2rem;
+ height: 3rem;
}
-.terms__accept:hover {
- background-color: #4107a1;
+.terms__button--decline {
+ background-color: white;
+ border: 1px solid #4608ad;
+ color: #4608ad;
}
.terms__privacy {
@@ -297,6 +328,14 @@ a:hover {
display: inline-block;
}
+ .theme-mode .header__settings {
+ color: var(--color-text-dark);
+ }
+
+ .theme-mode .header__icon {
+ color: #fff;
+ }
+
.theme-mode .category__link {
color: #fff;
}
@@ -305,6 +344,11 @@ a:hover {
color: #fff
}
+ .theme-mode .technology__link:hover .technology__name {
+ border-bottom: 1px solid var(--color-text-dark);
+ color: var(--color-text-dark);
+ }
+
.theme-mode .technology__confidence {
}
@@ -317,11 +361,11 @@ a:hover {
border-color: var(--color-secondary-dark)
}
- .theme-mode .footer__settings {
- color: var(--color-text-dark);
+ .theme-mode .footer__open-in-new {
+ color: #fff;
}
- .theme-mode .alerts__icon {
- color:var(--color-text-dark);
+ .theme-mode .technology__open-in-new {
+ color: #fff;
}
}
diff --git a/src/drivers/webextension/html/options.html b/src/drivers/webextension/html/options.html
index 3dfc531d4..b07b09cab 100644
--- a/src/drivers/webextension/html/options.html
+++ b/src/drivers/webextension/html/options.html
@@ -43,7 +43,7 @@
-
+