orestbida / cookieconsent

:cookie: Simple cross-browser cookie-consent plugin written in vanilla js
https://playground.cookieconsent.orestbida.com/
MIT License
3.68k stars 387 forks source link

[Feat]: Placeholders in language json #637

Closed 5ulo closed 4 months ago

5ulo commented 4 months ago

Description

Lets say you have implement cookie consent to multiple sites and dont want to edit each language json individually. In that case placeholders would be great and will be replaced dynamically. Maybe it is implemented or there's some solution but I havent found that.. or I am just blind. In v2 I used to use variables inside the 'inline' json, but now I find it better to use standalone lang files en.json whatever.json

Proposed solution

I have cc-init.js where I grab all necessary values like sitename, gdpr link, email for infos about cookies etc. These values are populated dynamically by site language. Inside cc-init.js is also cc.run

{
    "[SITENAME]": "Acme",
    "[GDPR]": "/privacy-policy",
    "[EMAIL]": "responsible_person@domain.tld"
}

Then would be great in en.json or whatever.json to use for example

{
    "consentModal": {
        "title": "Cookies on [SITENAME]"
...

So some find/replace method.

Additional details

edit: This is what I tried a while ago and was working:

const PLACEHOLDER = {
    sk: {
        "[SITENAME]": "ACME",
        "[GDPR]": "/ochrana-osobnych-udajov/"
    },
    en: {
        "[SITENAME]": "ACME",
        "[GDPR]": "/privacy-policy/"
    }
}
...
...
language: {
    default: 'sk',
    autoDetect: 'document',

    translations: {
        'en': readLangJson('/libs/js/cookieconsent/en.json', PLACEHOLDER.en),
        'sk': readLangJson('/libs/js/cookieconsent/sk.json', PLACEHOLDER.sk),
    },
},

and a synchronous fetch function

function readLangJson(filePath, replacements) {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', filePath, false);  // Synchronous request
    xhr.send();
    if (xhr.status === 200) {
        let modified = xhr.responseText;
        for (const [key, value] of Object.entries(replacements)) {
            modified = modified.split(key).join(value);
        }
        return JSON.parse(modified);
    } else {
        console.error(`Error reading file ${filePath}: ${xhr.status} ${xhr.statusText}`);
        return null;
    }
}

I'm not a pro so I bet it could be written much better.

No response

orestbida commented 4 months ago

There are no placeholder replacement functions built it, and unfortunately I don't plan on implementing one.

With that said, I can allow the use of callback functions, so that you won't have to load all the json files at once:

language: {
    default: sk,
    autoDetect: 'document',

    translations: {
        en: () => readLangJson('/libs/js/cookieconsent/en.json', PLACEHOLDER.en),
        sk: () => readLangJson('/libs/js/cookieconsent/sk.json', PLACEHOLDER.sk),
    },
}

This way only the required translation would be loaded.

You could also use an async callback:

language: {
    default: 'sk',
    autoDetect: 'document',

    translations: {
        en: async () => {
            const resp = await fetch('path-to-json');
            return await resp.json();
        }
    },
}
5ulo commented 4 months ago

I used xhr sync instead of fetch async because using async ends with console error.

        en: async () => {
            const resp = await fetch('path-to-json');
            return await resp.json();
        }

this ends with

cookieconsent.umd.js:7 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'setAttribute')
    at A (cookieconsent.umd.js:7:1295)
    at Fe (cookieconsent.umd.js:7:16544)
    at e.run (cookieconsent.umd.js:7:21522)
orestbida commented 4 months ago

Yes, it's expected to throw error because callbacks are not supported currently, but we can change that.

The main benefit of using callback functions is that only the required translation would be loaded, unlike your current implementation where both translations are loaded at once.

orestbida commented 4 months ago

The discussed support for callback functions will be tracked in #638, closing this.