Search code examples
javascriptboomi

Substring French text using javascript replaces the special characters


I can't do substring due to french special characters. My resulting string is usually more than the number specified due to special characters in french.

Here is the approach i am using after doing some search. Both conversion and byteLength functions i picked from stackoverflow, some how i missed the threads from where i picked these. The issue with this approach is that it replaces the special characters. How can i keep the special characters intact?

  1. convert the string to byte array
  2. check the length of each byte and do math

Ultimately this script will be used inside Boomi.

Sample french text

SVP remplacer 3 lumière de néons brûlées. Deux néons sont situés dans le bureau de la cliente et dans le desk room. Le dernier est une ampoule neon et c'est située dans le lobby. Le plafond est de hauteur standard

After running it though script, it becomes

SVP remplacer 3 lumière de néons brûlées. Deux néons sont situés dans le bureau de la cliente et dans le desk room. Le dernier est une ampoule neon et c'est située dans le lobby.

JS Fiddle

https://jsfiddle.net/learningjsfiddle/0no1t9k8/2/

Here is the call

var newFrench = stringTrim(frenchText, 200);

and here are the functions to help with all this.

var stringToUtf8ByteArray = function(str) {
            // TODO(user): Use native implementations if/when available
            var out = [], p = 0;
            for (var i = 0; i < str.length; i++) {
                var c = str.charCodeAt(i);
                if (c < 128) {
                    out[p++] = c;
                } else if (c < 2048) {
                    out[p++] = (c >> 6) | 192;
                    out[p++] = (c & 63) | 128;
                } else if (((c & 0xFC00) == 0xD800) && (i + 1) < str.length && ((str.charCodeAt(i + 1) & 0xFC00) == 0xDC00)) {
                    // Surrogate Pair
                    c = 0x10000 + ((c & 0x03FF) << 10) + (str.charCodeAt(++i) & 0x03FF);
                    out[p++] = (c >> 18) | 240;
                    out[p++] = ((c >> 12) & 63) | 128;
                    out[p++] = ((c >> 6) & 63) | 128;
                    out[p++] = (c & 63) | 128;
                } else{
                    out[p++] = (c >> 12) | 224;
                    out[p++] = ((c >> 6) & 63) | 128;
                    out[p++] = (c & 63) | 128;
                }
            }
            return out;
        };

        var byteLength = function(str) {
            // returns the byte length of an utf8 string
            var s = str.length;
            for (var i=str.length-1; i>=0; i--) {
                var code = str.charCodeAt(i);
                if (code > 0x7f && code <= 0x7ff) s++;
                else if (code > 0x7ff && code <= 0xffff) s+=2;
                if (code >= 0xDC00 && code <= 0xDFFF) i--; //trail surrogate
            }
            return s;
        }

        var stringTrim = function(str, maxLength){
            //convert to byte array
            var arr = stringToUtf8ByteArray(str);
            if(arr.length <= maxLength)
                return str;

            //slice upto maxLength
            var arrNew = arr.slice(0, maxLength);
            //check each char to make sure that french chars are properly picked
            var lengthChar = 0;
            var newVal = "";
            for (i=0; i<maxLength; i++){
                var singleChar = String.fromCharCode(arrNew[i]);
                lengthChar += byteLength(singleChar);
                if(lengthChar <= maxLength)
                    newVal += singleChar;
            }
            return newVal;
        }

Update 1: Here is a new fiddle and this is keeping the chars intact. I am only using the byteLength from above. Looks like this will be the solution unless some one points me to a better way of handling this.

https://jsfiddle.net/learningjsfiddle/0no1t9k8/27/

var byteFrench = french;
var byteFrenchLength = byteLength(byteFrench);
if (byteFrenchLength > lengthAllowed){
    var newLength = lengthAllowed - (byteFrenchLength-lengthAllowed); //removing extra chars
    byteFrench = byteFrench.substr(0, newLength);
}
document.getElementById('logSubstr').innerHTML = '[' + byteFrenchLength + '] new length ['+ byteLength(byteFrench) +']' + byteFrench;

Solution

  • var byteLength = function(str) {
                // returns the byte length of an utf8 string
                var s = str.length;
                for (var i=str.length-1; i>=0; i--) {
                    var code = str.charCodeAt(i);
                    if (code > 0x7f && code <= 0x7ff) s++;
                    else if (code > 0x7ff && code <= 0xffff) s+=2;
                    if (code >= 0xDC00 && code <= 0xDFFF) i--; //trail surrogate
                }
                return s;
            }
    
    
            var subStringTrim = function(str, maxLength){
                if(!hasValue(str) || !hasValue(maxLength) || maxLength <= 0)
                    return str;
    
                var strByteLength = byteLength(str);
                if(strByteLength <= maxLength)
                    return str;
    
                //pick upto maxlength
                str = str.substr(0, maxLength);
    
                //check the length again and then do sub string by calculating max newLength. This is important for french chars. 
                strByteLength = byteLength(str);
                if(strByteLength <= maxLength)
                    return str; //english chars will get returned here
    
                //removing extra chars for other than english
                var newLength = maxLength - (strByteLength-maxLength); 
                if(newLength > 0)
                    str = str.substr(0, newLength);
    
                return str;
            }