vitalets/google-translate-api

Support react-native

vitalets opened this issue ยท 10 comments

Currently library works only in Node.js, because it replies on packages like fs, configstore etc. It does not work in React Native out of box but technically it is possible.

It would be great to to add build for react native where usage of fs and configstore are replaced with RN asyncStorage api.

Related issues:

And it'll be also good if there is for Angular.

have you resolve issue Unable to resolve module 'querystring'?

Hi, I have been trying to convert this library for react-native without using got or querystring. so far i have been doing well but now im getting somehow different json response from

json = JSON.parse(json.slice(length.length, parseInt(length, 10) + length.length));

this is the translator method please have a look you may find something i dont.

import translatorLanguage from './translatorLanguage';

export default async (
    text: string,
    opts?: { from?: string | boolean; to?: string | boolean; tld?: string }
) => {
    try {
        opts = opts || {};
        var e = undefined as { message: string; code: number } | undefined;
        var languages = new translatorLanguage();
        [opts.from, opts.to].forEach(function (lang) {
            if (lang && !languages.isSupported(lang)) {
                e = {
                    message: "The language '" + lang + "' is not supported",
                    code: 400,
                };
            }
        });
        if (e) {
           return e;
        }
        const extract = (key: string, res: string) => {
            var re = new RegExp(`"${key}":".*?"`);
            var result = re.exec(res);
            if (result !== null) {
                return result[0].replace(`"${key}":"`, '').slice(0, -1);
            }
            return '';
        };

        const objToQueryString = (obj: any) => {
            const keyValuePairs = [];
            for (const key in obj) {
                keyValuePairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(obj[key]));
            }
            return keyValuePairs.join('&');
        }

        opts.from = opts.from || 'auto';
        opts.to = opts.to || 'en';
        opts.tld = opts.tld || 'com';

        opts.from = languages.getCode(opts.from.toString());
        opts.to = languages.getCode(opts.to.toString());

        var url = 'https://translate.google.' + opts.tld;
        var response = await fetch(url);
        var text = await response.text();

        var data = {
            rpcids: 'MkEWBc',
            'f.sid': extract('FdrFJe', text),
            bl: extract('cfb2h', text),
            hl: 'en-US',
            'soc-app': 1,
            'soc-platform': 1,
            'soc-device': 1,
            _reqid: Math.floor(1000 + Math.random() * 9000),
            rt: 'c',
        };
       
        url = url + '/_/TranslateWebserverUi/data/batchexecute?' + objToQueryString(data);
        let headers = new Headers({
            'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
        });
        var body = 'f.req=' + encodeURIComponent(JSON.stringify([[['MkEWBc', JSON.stringify([[text, opts.from, opts.to, true], [null]]), null, 'generic']]])) + '&';
        var res = await fetch(url, {
            method: "POST",
            headers: headers,
            body: body
        });
        if (res) {
            var ttx= await res.text();
            console.log(ttx)
            var json = (ttx).slice(6);
            var length = '';
         
            var result = {
                text: '',
                pronunciation: '',
                from: {
                    language: {
                        didYouMean: false,
                        iso: ''
                    },
                    text: {
                        autoCorrected: false,
                        value: '',
                        didYouMean: false
                    }
                },
                raw: ''
            };

            try {
                length = /^\d+/.exec(json)[0];
             
                json = JSON.parse(json.slice(length.length, parseInt(length, 10) + length.length));
                console.log(json)
                json = JSON.parse(json[0][2]);
                
                result.raw = json;
            } catch (e) {
                return result;
            }

            if (json[1][0][0][5] === undefined) {
                // translation not found, could be a hyperlink?
                result.text = json[1][0][0][0];
            } else {
                json[1][0][0][5].forEach(function (obj) {
                    if (obj[0]) {
                        result.text += obj[0];
                    }
                });
            }
            result.pronunciation = json[1][0][0][1];

            // From language
            if (json[0] && json[0][1] && json[0][1][1]) {
                result.from.language.didYouMean = true;
                result.from.language.iso = json[0][1][1][0];
            } else if (json[1][3] === 'auto') {
                result.from.language.iso = json[2];
            } else {
                result.from.language.iso = json[1][3];
            }

            // Did you mean & autocorrect
            if (json[0] && json[0][1] && json[0][1][0]) {
                var str = json[0][1][0][0][1];

                str = str.replace(/<b>(<i>)?/g, '[');
                str = str.replace(/(<\/i>)?<\/b>/g, ']');

                result.from.text.value = str;

                if (json[0][1][0][2] === 1) {
                    result.from.text.autoCorrected = true;
                } else {
                    result.from.text.didYouMean = true;
                }
            }

            return result;
        }      
    } catch (error) {
        console.log(error);
    }
    return undefined;
};


never mind I found the issue

never mind I found the issue

What was your solution?

Well I hade made small mistake, and overwrote property text . Anyway here the full working react-native code

import translatorLanguage from './translatorLanguage';

export default async (
    text: string,
    opts?: { from?: string | boolean; to?: string | boolean; tld?: string }
) => {
    try {
        var result = {
            text: '',
            pronunciation: '',
            from: {
                language: {
                    didYouMean: false,
                    iso: ''
                },
                text: {
                    autoCorrected: false,
                    value: '',
                    didYouMean: false
                }
            },
            raw: ''
        };


        opts = opts || {};
        var e = undefined as { message: string; code: number } | undefined;
        var languages = new translatorLanguage();
        [opts.from, opts.to].forEach(function (lang) {
            if (lang && !languages.isSupported(lang)) {
                e = {
                    message: "The language '" + lang + "' is not supported",
                    code: 400,
                };
            }
        });
        if (e) {
            return undefined;
        }
        const extract = (key: string, res: string) => {
            var re = new RegExp(`"${key}":".*?"`);
            var result = re.exec(res);
            if (result !== null) {
                return result[0].replace(`"${key}":"`, '').slice(0, -1);
            }
            return '';
        };

        const objToQueryString = (obj: any) => {
            const keyValuePairs = [];
            for (const key in obj) {
                keyValuePairs.push(key + '=' + obj[key]);
            }
            return keyValuePairs.join('&');
        }

        opts.from = opts.from || 'auto';
        opts.to = opts.to || 'en';
        opts.tld = opts.tld || 'com';

        opts.from = languages.getCode(opts.from.toString());
        opts.to = languages.getCode(opts.to.toString());

        var url = 'https://translate.google.' + opts.tld;
        var response = await fetch(url);
        var txt = await response.text();

        var data = {
            rpcids: 'MkEWBc',
            'f.sid': extract('FdrFJe', txt),
            bl: extract('cfb2h', txt),
            hl: 'en-US',
            'soc-app': 1,
            'soc-platform': 1,
            'soc-device': 1,
            _reqid: Math.floor(1000 + Math.random() * 9000),
            rt: 'c',
        };
      
        url = url + '/_/TranslateWebserverUi/data/batchexecute?' + objToQueryString(data);
        var body = 'f.req=' + encodeURIComponent(JSON.stringify([[['MkEWBc', JSON.stringify([[text, opts.from, opts.to, true], [null]]), null, 'generic']]])) + '&';
        var res = await fetch(url, {
            method: "POST",
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
              },
            body: body
        });
        if (res) {
            var ttx = await res.text();
            var json = (ttx).slice(6);
            var length = '';

      

            try {
                length = /^\d+/.exec(json)[0];

                json = JSON.parse(json.slice(length.length, parseInt(length, 10) + length.length));

                json = JSON.parse(json[0][2]);

                result.raw = json;
            } catch (e) {
                return result;
            }
            if (json[1][0][0][5] === undefined) {
                // translation not found, could be a hyperlink?
                result.text = json[1][0][0][0];
            } else {
                json[1][0][0][5].forEach(function (obj) {
                    if (obj[0]) {
                        result.text += obj[0];
                    }
                });
            }
            result.pronunciation = json[1][0][0][1];

            // From language
            if (json[0] && json[0][1] && json[0][1][1]) {
                result.from.language.didYouMean = true;
                result.from.language.iso = json[0][1][1][0];
            } else if (json[1][3] === 'auto') {
                result.from.language.iso = json[2];
            } else {
                result.from.language.iso = json[1][3];
            }

            // Did you mean & autocorrect
            if (json[0] && json[0][1] && json[0][1][0]) {
                var str = json[0][1][0][0][1];

                str = str.replace(/<b>(<i>)?/g, '[');
                str = str.replace(/(<\/i>)?<\/b>/g, ']');

                result.from.text.value = str;

                if (json[0][1][0][2] === 1) {
                    result.from.text.autoCorrected = true;
                } else {
                    result.from.text.didYouMean = true;
                }
            }

            return result;
        }
    } catch (error) {
        console.log(error);
    }
    return undefined;
};


translatorLanguage.tsx

import * as IOSLang from '../../languages/ISOLANG.json'
export default class translatorLanguage {

  lang: any = Object.assign({}, IOSLang);

  getCode(desiredLang: string) {
    if (!desiredLang) {
      return false;
    }
    var lng = this.lang;

    if (lng[desiredLang]) {
      return desiredLang;
    }
    ;
    var keys = Object.keys(lng).filter(function (key) {
      return key.toLowerCase() === desiredLang.toLowerCase();
    });

    return keys[0] || false;
  }

  isSupported(desiredLang: string | boolean) {
    return Boolean(this.getCode(desiredLang.toString()));
  }

  static GetLanguages = () => {
    var item = new translatorLanguage().lang;
    return Object.keys(item).map(function (x) { return { label: item[x].name, value: x } }).filter(x => x.label && x.value).sort((a, b) => {
      return a.label - b.label
    });
  }

  constructor() {

  }
}



Well I hade made small mistake, and overwrote property text . Anyway here the full working react-native code

import translatorLanguage from './translatorLanguage';

export default async (
    text: string,
    opts?: { from?: string | boolean; to?: string | boolean; tld?: string }
) => {
    try {
        var result = {
            text: '',
            pronunciation: '',
            from: {
                language: {
                    didYouMean: false,
                    iso: ''
                },
                text: {
                    autoCorrected: false,
                    value: '',
                    didYouMean: false
                }
            },
            raw: ''
        };


        opts = opts || {};
        var e = undefined as { message: string; code: number } | undefined;
        var languages = new translatorLanguage();
        [opts.from, opts.to].forEach(function (lang) {
            if (lang && !languages.isSupported(lang)) {
                e = {
                    message: "The language '" + lang + "' is not supported",
                    code: 400,
                };
            }
        });
        if (e) {
            return undefined;
        }
        const extract = (key: string, res: string) => {
            var re = new RegExp(`"${key}":".*?"`);
            var result = re.exec(res);
            if (result !== null) {
                return result[0].replace(`"${key}":"`, '').slice(0, -1);
            }
            return '';
        };

        const objToQueryString = (obj: any) => {
            const keyValuePairs = [];
            for (const key in obj) {
                keyValuePairs.push(key + '=' + obj[key]);
            }
            return keyValuePairs.join('&');
        }

        opts.from = opts.from || 'auto';
        opts.to = opts.to || 'en';
        opts.tld = opts.tld || 'com';

        opts.from = languages.getCode(opts.from.toString());
        opts.to = languages.getCode(opts.to.toString());

        var url = 'https://translate.google.' + opts.tld;
        var response = await fetch(url);
        var txt = await response.text();

        var data = {
            rpcids: 'MkEWBc',
            'f.sid': extract('FdrFJe', txt),
            bl: extract('cfb2h', txt),
            hl: 'en-US',
            'soc-app': 1,
            'soc-platform': 1,
            'soc-device': 1,
            _reqid: Math.floor(1000 + Math.random() * 9000),
            rt: 'c',
        };
      
        url = url + '/_/TranslateWebserverUi/data/batchexecute?' + objToQueryString(data);
        var body = 'f.req=' + encodeURIComponent(JSON.stringify([[['MkEWBc', JSON.stringify([[text, opts.from, opts.to, true], [null]]), null, 'generic']]])) + '&';
        var res = await fetch(url, {
            method: "POST",
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
              },
            body: body
        });
        if (res) {
            var ttx = await res.text();
            var json = (ttx).slice(6);
            var length = '';

      

            try {
                length = /^\d+/.exec(json)[0];

                json = JSON.parse(json.slice(length.length, parseInt(length, 10) + length.length));

                json = JSON.parse(json[0][2]);

                result.raw = json;
            } catch (e) {
                return result;
            }
            if (json[1][0][0][5] === undefined) {
                // translation not found, could be a hyperlink?
                result.text = json[1][0][0][0];
            } else {
                json[1][0][0][5].forEach(function (obj) {
                    if (obj[0]) {
                        result.text += obj[0];
                    }
                });
            }
            result.pronunciation = json[1][0][0][1];

            // From language
            if (json[0] && json[0][1] && json[0][1][1]) {
                result.from.language.didYouMean = true;
                result.from.language.iso = json[0][1][1][0];
            } else if (json[1][3] === 'auto') {
                result.from.language.iso = json[2];
            } else {
                result.from.language.iso = json[1][3];
            }

            // Did you mean & autocorrect
            if (json[0] && json[0][1] && json[0][1][0]) {
                var str = json[0][1][0][0][1];

                str = str.replace(/<b>(<i>)?/g, '[');
                str = str.replace(/(<\/i>)?<\/b>/g, ']');

                result.from.text.value = str;

                if (json[0][1][0][2] === 1) {
                    result.from.text.autoCorrected = true;
                } else {
                    result.from.text.didYouMean = true;
                }
            }

            return result;
        }
    } catch (error) {
        console.log(error);
    }
    return undefined;
};

translatorLanguage.tsx

import * as IOSLang from '../../languages/ISOLANG.json'
export default class translatorLanguage {

  lang: any = Object.assign({}, IOSLang);

  getCode(desiredLang: string) {
    if (!desiredLang) {
      return false;
    }
    var lng = this.lang;

    if (lng[desiredLang]) {
      return desiredLang;
    }
    ;
    var keys = Object.keys(lng).filter(function (key) {
      return key.toLowerCase() === desiredLang.toLowerCase();
    });

    return keys[0] || false;
  }

  isSupported(desiredLang: string | boolean) {
    return Boolean(this.getCode(desiredLang.toString()));
  }

  static GetLanguages = () => {
    var item = new translatorLanguage().lang;
    return Object.keys(item).map(function (x) { return { label: item[x].name, value: x } }).filter(x => x.label && x.value).sort((a, b) => {
      return a.label - b.label
    });
  }

  constructor() {

  }
}

Save in where this file and how to calling the function?

I was able to implement it with webpack using 'node-stdlib-browser' and 'setimmediate' I imagine it could be fairly easily implemented in a similar way for react.

Okay, I made an axios port that doesn't require either of those here: https://github.com/AidanWelch/google-translate-api

I've added React-native support in new beta release v9.
Please check it out and feel free to reopen if needed.
In my tests with react-native@0.69.6 everything works fine ๐Ÿฅณ