diff --git a/components/metrics/LanguagesTable.js b/components/metrics/LanguagesTable.js
new file mode 100644
index 00000000..2b6cd868
--- /dev/null
+++ b/components/metrics/LanguagesTable.js
@@ -0,0 +1,31 @@
+import React from 'react';
+import MetricsTable from './MetricsTable';
+import { percentFilter } from 'lib/filters';
+import { FormattedMessage } from 'react-intl';
+import useLanguageNames from 'hooks/useLanguageNames';
+import useLocale from 'hooks/useLocale';
+
+export default function LanguagesTable({ websiteId, onDataLoad, ...props }) {
+ const { locale } = useLocale();
+ const languageNames = useLanguageNames(locale);
+
+ function renderLabel({ x }) {
+ return (
+
+ {languageNames[x] ?? }{' '}
+
+ );
+ }
+
+ return (
+ }
+ type="language"
+ metric={}
+ websiteId={websiteId}
+ onDataLoad={data => onDataLoad?.(percentFilter(data))}
+ renderLabel={renderLabel}
+ />
+ );
+}
diff --git a/components/pages/WebsiteDetails.js b/components/pages/WebsiteDetails.js
index a8442ea1..876fbfc4 100644
--- a/components/pages/WebsiteDetails.js
+++ b/components/pages/WebsiteDetails.js
@@ -16,6 +16,7 @@ import BrowsersTable from '../metrics/BrowsersTable';
import OSTable from '../metrics/OSTable';
import DevicesTable from '../metrics/DevicesTable';
import CountriesTable from '../metrics/CountriesTable';
+import LanguagesTable from '../metrics/LanguagesTable';
import EventsTable from '../metrics/EventsTable';
import EventsChart from '../metrics/EventsChart';
import useFetch from 'hooks/useFetch';
@@ -30,6 +31,7 @@ const views = {
os: OSTable,
device: DevicesTable,
country: CountriesTable,
+ language: LanguagesTable,
event: EventsTable,
};
@@ -82,6 +84,10 @@ export default function WebsiteDetails({ websiteId }) {
label: ,
value: resolve({ view: 'country' }),
},
+ {
+ label: ,
+ value: resolve({ view: 'language' }),
+ },
{
label: ,
value: resolve({ view: 'event' }),
diff --git a/hooks/useLanguageNames.js b/hooks/useLanguageNames.js
new file mode 100644
index 00000000..ef179185
--- /dev/null
+++ b/hooks/useLanguageNames.js
@@ -0,0 +1,34 @@
+import { useState, useEffect } from 'react';
+import { useRouter } from 'next/router';
+import { get } from 'lib/web';
+import enUS from 'public/language/en-US.json';
+
+const languageNames = {
+ 'en-US': enUS,
+};
+
+export default function useLanguageNames(locale) {
+ const [list, setList] = useState(languageNames[locale] || enUS);
+ const { basePath } = useRouter();
+
+ async function loadData(locale) {
+ const { ok, data } = await get(`${basePath}/language/${locale}.json`);
+
+ if (ok) {
+ languageNames[locale] = data;
+ setList(languageNames[locale]);
+ } else {
+ setList(enUS);
+ }
+ }
+
+ useEffect(() => {
+ if (!languageNames[locale]) {
+ loadData(locale);
+ } else {
+ setList(languageNames[locale]);
+ }
+ }, [locale]);
+
+ return list;
+}
diff --git a/pages/api/website/[id]/metrics.js b/pages/api/website/[id]/metrics.js
index 3e9b9925..5e16e350 100644
--- a/pages/api/website/[id]/metrics.js
+++ b/pages/api/website/[id]/metrics.js
@@ -2,7 +2,7 @@ import { getPageviewMetrics, getSessionMetrics, getWebsiteById } from 'lib/queri
import { ok, methodNotAllowed, unauthorized, badRequest } from 'lib/response';
import { allowQuery } from 'lib/auth';
-const sessionColumns = ['browser', 'os', 'device', 'country'];
+const sessionColumns = ['browser', 'os', 'device', 'country', 'language'];
const pageviewColumns = ['url', 'referrer'];
function getTable(type) {
@@ -37,7 +37,19 @@ export default async (req, res) => {
const endDate = new Date(+end_at);
if (sessionColumns.includes(type)) {
- const data = await getSessionMetrics(websiteId, startDate, endDate, type, { url });
+ let data = await getSessionMetrics(websiteId, startDate, endDate, type, { url });
+
+ if (type === 'language') {
+ let combined = {};
+
+ for (let { x, y } of data) {
+ x = String(x).toLowerCase().split('-')[0];
+ if (!combined[x]) combined[x] = { x, y };
+ else combined[x].y += y;
+ }
+
+ data = Object.values(combined);
+ }
return ok(res, data);
}
diff --git a/public/language/en-US.json b/public/language/en-US.json
new file mode 100644
index 00000000..0a003d33
--- /dev/null
+++ b/public/language/en-US.json
@@ -0,0 +1,184 @@
+{
+ "ab": "Abkhaz",
+ "aa": "Afar",
+ "af": "Afrikaans",
+ "ak": "Akan",
+ "sq": "Albanian",
+ "am": "Amharic",
+ "ar": "Arabic",
+ "an": "Aragonese",
+ "hy": "Armenian",
+ "as": "Assamese",
+ "av": "Avaric",
+ "ae": "Avestan",
+ "ay": "Aymara",
+ "az": "Azerbaijani",
+ "bm": "Bambara",
+ "ba": "Bashkir",
+ "eu": "Basque",
+ "be": "Belarusian",
+ "bn": "Bengali",
+ "bh": "Bihari",
+ "bi": "Bislama",
+ "bs": "Bosnian",
+ "br": "Breton",
+ "bg": "Bulgarian",
+ "my": "Burmese",
+ "ca": "Catalan; Valencian",
+ "ch": "Chamorro",
+ "ce": "Chechen",
+ "ny": "Chichewa; Chewa; Nyanja",
+ "zh": "Chinese",
+ "cv": "Chuvash",
+ "kw": "Cornish",
+ "co": "Corsican",
+ "cr": "Cree",
+ "hr": "Croatian",
+ "cs": "Czech",
+ "da": "Danish",
+ "dv": "Divehi; Dhivehi; Maldivian;",
+ "nl": "Dutch",
+ "en": "English",
+ "eo": "Esperanto",
+ "et": "Estonian",
+ "ee": "Ewe",
+ "fo": "Faroese",
+ "fj": "Fijian",
+ "fi": "Finnish",
+ "fr": "French",
+ "ff": "Fula; Fulah; Pulaar; Pular",
+ "gl": "Galician",
+ "ka": "Georgian",
+ "de": "German",
+ "el": "Greek, Modern",
+ "gn": "Guaraní",
+ "gu": "Gujarati",
+ "ht": "Haitian; Haitian Creole",
+ "ha": "Hausa",
+ "he": "Hebrew (modern)",
+ "hz": "Herero",
+ "hi": "Hindi",
+ "ho": "Hiri Motu",
+ "hu": "Hungarian",
+ "ia": "Interlingua",
+ "id": "Indonesian",
+ "ie": "Interlingue",
+ "ga": "Irish",
+ "ig": "Igbo",
+ "ik": "Inupiaq",
+ "io": "Ido",
+ "is": "Icelandic",
+ "it": "Italian",
+ "iu": "Inuktitut",
+ "ja": "Japanese",
+ "jv": "Javanese",
+ "kl": "Kalaallisut, Greenlandic",
+ "kn": "Kannada",
+ "kr": "Kanuri",
+ "ks": "Kashmiri",
+ "kk": "Kazakh",
+ "km": "Khmer",
+ "ki": "Kikuyu, Gikuyu",
+ "rw": "Kinyarwanda",
+ "ky": "Kirghiz, Kyrgyz",
+ "kv": "Komi",
+ "kg": "Kongo",
+ "ko": "Korean",
+ "ku": "Kurdish",
+ "kj": "Kwanyama, Kuanyama",
+ "la": "Latin",
+ "lb": "Luxembourgish, Letzeburgesch",
+ "lg": "Luganda",
+ "li": "Limburgish, Limburgan, Limburger",
+ "ln": "Lingala",
+ "lo": "Lao",
+ "lt": "Lithuanian",
+ "lu": "Luba-Katanga",
+ "lv": "Latvian",
+ "gv": "Manx",
+ "mk": "Macedonian",
+ "mg": "Malagasy",
+ "ms": "Malay",
+ "ml": "Malayalam",
+ "mt": "Maltese",
+ "mi": "Māori",
+ "mr": "Marathi (Marāṭhī)",
+ "mh": "Marshallese",
+ "mn": "Mongolian",
+ "na": "Nauru",
+ "nv": "Navajo, Navaho",
+ "nb": "Norwegian Bokmål",
+ "nd": "North Ndebele",
+ "ne": "Nepali",
+ "ng": "Ndonga",
+ "nn": "Norwegian Nynorsk",
+ "no": "Norwegian",
+ "ii": "Nuosu",
+ "nr": "South Ndebele",
+ "oc": "Occitan",
+ "oj": "Ojibwe, Ojibwa",
+ "cu": "Old Church Slavonic, Church Slavic, Church Slavonic, Old Bulgarian, Old Slavonic",
+ "om": "Oromo",
+ "or": "Oriya",
+ "os": "Ossetian, Ossetic",
+ "pa": "Panjabi, Punjabi",
+ "pi": "Pāli",
+ "fa": "Persian",
+ "pl": "Polish",
+ "ps": "Pashto, Pushto",
+ "pt": "Portuguese",
+ "qu": "Quechua",
+ "rm": "Romansh",
+ "rn": "Kirundi",
+ "ro": "Romanian, Moldavian, Moldovan",
+ "ru": "Russian",
+ "sa": "Sanskrit (Saṁskṛta)",
+ "sc": "Sardinian",
+ "sd": "Sindhi",
+ "se": "Northern Sami",
+ "sm": "Samoan",
+ "sg": "Sango",
+ "sr": "Serbian",
+ "gd": "Scottish Gaelic; Gaelic",
+ "sn": "Shona",
+ "si": "Sinhala, Sinhalese",
+ "sk": "Slovak",
+ "sl": "Slovene",
+ "so": "Somali",
+ "st": "Southern Sotho",
+ "es": "Spanish; Castilian",
+ "su": "Sundanese",
+ "sw": "Swahili",
+ "ss": "Swati",
+ "sv": "Swedish",
+ "ta": "Tamil",
+ "te": "Telugu",
+ "tg": "Tajik",
+ "th": "Thai",
+ "ti": "Tigrinya",
+ "bo": "Tibetan Standard, Tibetan, Central",
+ "tk": "Turkmen",
+ "tl": "Tagalog",
+ "tn": "Tswana",
+ "to": "Tonga (Tonga Islands)",
+ "tr": "Turkish",
+ "ts": "Tsonga",
+ "tt": "Tatar",
+ "tw": "Twi",
+ "ty": "Tahitian",
+ "ug": "Uighur, Uyghur",
+ "uk": "Ukrainian",
+ "ur": "Urdu",
+ "uz": "Uzbek",
+ "ve": "Venda",
+ "vi": "Vietnamese",
+ "vo": "Volapük",
+ "wa": "Walloon",
+ "cy": "Welsh",
+ "wo": "Wolof",
+ "fy": "Western Frisian",
+ "xh": "Xhosa",
+ "yi": "Yiddish",
+ "yo": "Yoruba",
+ "za": "Zhuang, Chuang"
+}
\ No newline at end of file