🇫🇷 Special characters encoding issue in onboarding tips

See the button:

I wouldn’t be surprised if the issue was present inall the tooltips.

3 Likes

I stumbled on the same issue.

A quick look to Discourse shows that escape is used on the button label, title and content.

What does escape:

In a text context, this seems all valid characters to use.

I wonder, is it required here?
I believe html not marked with htmlSafe will be escaped by the template? :thinking:

A fix could be to replace the programming apostrophe with the actual French apostrophe.

Use:
Not: '

This symbol does not exist on US QWERTY keyboards and does not exist either on older AZERTY ones but it does exist on both new AFNOR (Association française de normalisation) standard French keyboards, either new AZERTY or BÉPO.

It wouldn’t fix the deeper issue that the programmatic apostrophe is not correctly escaped, but the real French apostrophe is the correct one to use and should be the one most French speakers will use in typing once a majority of them will be on standard keyboards.

The old AZERTY was never actually standardized by any official body.

1 Like

Got same issue on Discourse 3.4.1 …
I can’t possibly replace all the ' on the website. Do you have any suggestions?

I just fixed thiss issue with some custom code, replace ' to in I18n.translations

withPluginApi("0.8.18", (api) => {
    const locale = I18n.currentLocale();

    function replaceSingleQuotes(obj) {
        if (typeof obj === 'string') {
            // Split the string into HTML tags and non-tag parts
            return obj.split(/(<[^>]+>)/g).map(segment => {
                // If the segment is an HTML tag, do not replace anything
                if (segment.startsWith('<') && segment.endsWith('>')) {
                    return segment;
                } else {
                    // Replace single quotes only in non-HTML parts
                    return segment.replace(/'/g, '’');
                }
            }).join('');
        } else if (typeof obj === 'object' && obj !== null) {
            // Recursively process object properties
            for (const key in obj) {
                obj[key] = replaceSingleQuotes(obj[key]);
            }
        }
        return obj;
    }

    if (I18n.translations[locale].js) {
        I18n.translations[locale].js = replaceSingleQuotes(I18n.translations[locale].js);
    }
});

1 Like