Como criar ume extensão para o google-chrome?

Com açucar com afeto!

Criando uma extensão para o google chrome.

Para quem não sabe o que é uma extensão ela é uma conjunto de instruções e ferramentas que o navegador permite que adicionemos a ele sem que haja necessidade de alterarmos o seu código fonte.

Neste tutorial, vamos partir de uma necessidade que eu tive e criar uma extensão para que possamos traduzir um pedaço do texto que estamos lendo. A ideia é poder por exemplo, marcar uma palavra qualquer no texto e solicitar que seja feita a tradução dessa palavra. Mas por que fazer isso? Qual a necessidade de traduzir uma palavra, quando você pode facilmente, usando o google translator, traduzir todo o conteúdo? Simples. Eu gosto de praticar a leitura em inglês e as palavras que não sei, vou traduzindo devagar, assim aprendo seu significado e vou aumentando meu conhecimento neste idioma. Gostou da ideia? Então vamos criar essa extensão e você também poderá a partir de agora, começar a ler seus textos em inglês de outra forma.

Beleza, já entendemos o que é uma extensão e para o que ela serve. Mas o que preciso saber para começar a criá-las? Se você pensou que a resposta seria algo muito complexo, está redondamente enganado. Tudo o que você precisa saber é HTML, javascript e css. Não necessariamente em nível avançado, se sabe o básico já tá legal, a parte do javascript é que pode ser um pouco mais complicada, mas vou tentar explicar bem e qualquer dúvida, só postar um comment! E continuar seguindo este tutorial aqui!

Chega de conversa e vamos colocar a mão na massa. Primeiramente para entendermos melhor como funciona uma extensão, vamos criar o mesmo exemplo que é mostrado na página oficial da documentação, que postarei o link no final do artigo. Para isso, defina no seu computador uma pasta onde ficará o projeto e abra o seu editor de textos preferido. Aqui eu uso o sublime text! Particularmente é o meu preferido até o momento.

O primeiro e mais importante arquivo que vamos criar é o chamado manifest.json. É neste arquivo onde ficam todas as configurações da sua extensão: nome, descrição, versão, etc. Aqui tem um link que tem tudo sobre as configurações possíveis neste arquivo. https://developer.chrome.com/extensions/manifest, Mas nós só utilizaremos o básico que você pode acompanhar abaixo.


{
    "manifest_version": 2,
    "name": "Criando minha primeira extensão",
    "description": "Descrição da minha extensão",
    "version": "1.0",
    "browser_action":{
        "default_icon": "icon.png",
        "default_popup": "popup.html",
        "default_title": "Clique ME"
    },
    "permissions": [
        "activeTab"
    ]
}

Neste arquivo, configuramos informações básicas de nossa aplicação, definimos nosso ícone que será mostrado na barra do navegador e nossa página que será mostrada em um popup. Também neste arquivo, solicitamos uma permissão para acesso a activeTab ou Tab ativa.

Se você adicionasse essa extensão que acabamos de criar no navegador ela ainda não funcionaria, pois como você deve ter percebido, que ainda faltam dois arquivos que são de total relevância para ela. São eles o icon.png e o popup.html. Então vamos criar estes dois arquivos. Se você não quiser criar o ícone, basta copiar o que estou deixando logo abaixo.

Icone para aplicação

No arquivo popup.html, adicione o seguinte código HTML.

<!doctype html>
<html>
  <head>
    <title>Minha primeira extenãso</title>
    <style>
      body {
        font-family: "Segoe UI", "Lucida Grande", Tahoma, sans-serif;
        font-size: 100%;
      }
      #status {
        /* avoid an excessively wide status text */
        white-space: pre;
        text-overflow: ellipsis;
        overflow: hidden;
        max-width: 400px;
      }
    </style>

    <script src="popup.js"></script>
  </head>
  <body>
    <div id="status"></div>
  </body>
</html>

Crie também um arquivo popup.js, que será responsável por realizar as ações dentro da nossa extensão.


function getCurrentTabUrl(callback) {
  var queryInfo = {
    active: true,
    currentWindow: true
  };

  chrome.tabs.query(queryInfo, function(tabs) {
    var tab = tabs[0];

    var url = tab.url;

    console.assert(typeof url == 'string', 'tab.url should be a string');

    callback(url);
  });

}

function renderStatus(statusText) {
  document.getElementById('status').innerHTML = statusText;
}

function callback_url(url) {
  renderStatus("Ola mundo! pegamos a URL da Tab ativa! <br />URL: " + url); 
}

document.addEventListener('DOMContentLoaded', function() {
  getCurrentTabUrl(callback_url);
});

Vamos dar uma passada pelo código acima para entender o que ele está fazendo. Primeiramente vimos uma função chamada ** getCurrentTabUrl(callback){…} ** Essa função é responsável por solicitar as informações da tab ativa. Nela vemos um chrome.tabs.query, que faz uma consulta passando como filtro os parametros active e currentWindow como true, ou seja, a resposta para esta consulta deve ser sempre a tab que está ativa da janela atual. Após isso pegamos a url da tab e chamamos a função de callback que chegou até aqui passada por parâmetro. E passamos como argumento para ela a url que encontramos. Depois temos o renderStatus, que nada mais faz do que escrever uma informação visível para o usuário no html da extensão. Temos também a função callback_url que recebe a informação da url e solicita a escrita na pagina. E por fim temos o evento de carregamento do DOMDocument que informa que já podemos começar a brincadeira.

Neste momento a estrutura da nossa extensão é essa:

  • projeto
    • icon.png
    • manifest.json
    • popup.html
    • popup.js

Vamos agora testar nossa extensão em nosso navegador, se você tiver realizado todas as operações descritas aqui de forma correta vamos conseguir prosseguir sem problemas. Se tiver algum problema, dê uma revisada na estrutura de pastas e no arquivo manifest.json. Caso persista os sintomas procurar um médico!!! Brincadeirinha. Pode me enviar uma mensagem nos comentários que temos resolver juntos.

Clique no menu do chrome e depois na opção configurações. Selecione nas abas encontradas no canto superior esquerdo a opção extensões.

Como ainda não temos nossa aplicação finalizada, estamos apenas testando, clique sobre o botão com o texto “Carregar extensão expandida“. Selecione a pasta onde está nosso projeto de extensão e clique em open ou abrir.

Pronto, já veremos no nosso navegador o ícone da nossa e extensão que quando clicado deve abrir um popup com as informações disponibilizadas pela nossa aplicação.

Após o clique você deverá visualizar a seguinte mensagem:

Ola mundo! Pegamos a URL da Tab ativa! http://www.site-ativo.com.br

Beleza, já conseguimos criar uma extensão e vê-la funcionando. Agora para não perdermos esse código pois podemos utilizá-lo como base para outras aplicações, vamos copiar esta pasta e todos os seus arquivos para podermos criar nossa nova extensão.

Eu criei um repositório no github para isso, se quiserem é só dar um fork no projeto e começar daí.

https://github.com/brunoom1/chrome-basic-extension

Continuando… Nossa nova extensão deve ler na tab ativa uma frase ou texto selecionado e realizar a tradução desta informação e nos retornar. Para isso, teremos que usar um tradutor com API e nesse caso utilizaremos a API do google translate.

Tendo copiado ou forkado e clonado o repositório e já tendo tudo disponível na maquina para começarmos o trabalho. Vamos iniciar a edição do arquivo de configuração manifest.json. Nele vamos alterar o nome da extensão e a descrição do que ela faz. Não vou interferir nisso, cada um define em seu projeto essas informações.

Para que possamos pegar as informações selecionadas pelo usuário no chrome usando a extensão é bem simples, basta para isso usar o trecho de script abaixo.

 
    chrome.tabs.executeScript(tab.id, {
      code: "window.getSelection().toString();"
    }, function(result){

      // text selected
      if( (text = result[0]) != "" ){

        translate(text, function(translated){
          renderStatus(translated);
        });

      }
      else{
        renderStatus("Selecione um texto primeiro");
      }

    });

Pronto, o código da nossa extensão ficou assim.



function getCurrentTabUrl(callback) {
  var queryInfo = {
    active: true,
    currentWindow: true
  };

  chrome.tabs.query(queryInfo, function(tabs) {
    var tab = tabs[0];

    var url = tab.url;

    console.assert(typeof url == 'string', 'tab.url should be a string');


    // busca texto selecionado no tab
    chrome.tabs.executeScript(tab.id, {
      code: "window.getSelection().toString();"
    }, function(result){

      // text selected
      if( (text = result[0]) != "" ){

        translate(text, function(translated){
          renderStatus(translated);
        });

      }
      else{
        renderStatus("Selecione um texto primeiro");
      }

    });

  });

}


function translator(className){

  var translator = null;

  switch(className){
    case "GoogleAPI": translator = new AdapterTranslatorGoogleAPI();
    break;
    case "YandexAPI": translator = new AdapterTranslatorYandexAPI();
    break;
  }

  return translator;
}

function renderStatus(statusText) {
  document.getElementById('status').innerHTML = statusText;
}

document.addEventListener('DOMContentLoaded', function() {
  getCurrentTabUrl();
});

Vamos agora criar uma uma função que será responsável por realizar a tradução e retornar por callback a informação já traduzida. Passaremos para esta função o texto a ser traduzido e também os idiomas: atual e o de tradução. E por último passamos o adaptador de tradução.

function translate(text, callback, options){
	//...
}

Eu não sei quantos são os tradutores que disponibilizam API gratuitas porém vamos criar também uma classe AdapterTranslator que nos permita alterarmos a ferramenta de tradução. O único requisito desta classe será ter o método translate. Assim sabemos sempre como realizar a tradução.

Nessa parte criamos: Uma função translator que recebe como argumento o nome da classe do adaptador que vamos utilizar para realizarmos a tradução; Criamos também uma função translate que será responsável por chamar a classe translator que recebe o nome do adaptador e retorna uma instância desta classe; E por fim, definimos uma classe AdapterTranslatorGoogleAPI que é realmente a classe responsável por realizar pra gente a tradução deste conteúdo.

Então até aqui temos as seguintes funções e classe!

function AdapterTranslatorGoogleAPI(){

  this.key = "apikey";
  this.urlApi = "https://www.googleapis.com/language/translate/v2?key={key}&q={q}&source={s}&target={t}";

  this.translate = function(text, callback, options){
    // traduzir texto

    var url = this.urlApi.replace("{key}", this.key)
      .replace("{q}", text)
      .replace("{s}", options.current_lang)
      .replace("{t}", options.for_lang);
    var method = "get";

    
    var request = new XMLHttpRequest();
    request.responseType = "json";
    request.onreadystatechange = function(){

      // requisicao finalizada
      if(this.readyState == 4){
        callback({
          data: text_translated
        })

      }

    }
    request.open(method, url, true);
    request.send();
  }
}


function translator(className){

  var translator = null;

  switch(className){
    case "GoogleAPI": translator = new AdapterTranslatorGoogleAPI();
    break;
  }

  return translator;
}


function translate(text, callback, options){

  var default_options =  {
    "current_lang": "pt", 
    "for_lang": "en"
  };


  translator("YandexAPI").translate(text, callback, default_options);
}

Bom galera, chegando neste ponto do artigo descobri que infelizmente não temos como usar o google translate api gratuitamente, nem para testar nossa extensão, porém isso não é de todo ruim, pois como já preparamos uma estrutura para facilitar a composição de outras apis de tradução, será mais fácil criar um outro adaptador. Pesquisando bastante pela net, descobri um bom site que tem uma api pública e grátis (não comercial). Mas já é ótimo pra gente. O site é https://tech.yandex.com/translate/, já testei a api deles e parece estar funcionando muito bem, você só terá que criar o api key. Mas também é bem tranquilo e eles só solicitam um cadastro básico para criação da sua conta.

Seguindo, o que mudou no código? Criamos uma nova classe que será usada para esta api, seguindo o mesmo esquema que estavamos fazendo anteriormente, criamos a AdapterTranslatorYandexAPI com o mesmo método translate passando os mesmos parâmetros que seriam passados para a do Google. Dentro dessa classe no método translate, fazemos as requisições para o servidor da api, com suas respectivas informações, pegamos o resultado e o passamos para o callback. E por fim, pegamos esse valor traduzido que veio da API e jogamos para visualização do usuário.

E o código final ficou assim.


function getCurrentTabUrl(callback) {
  var queryInfo = {
    active: true,
    currentWindow: true
  };

  chrome.tabs.query(queryInfo, function(tabs) {
    var tab = tabs[0];

    var url = tab.url;

    console.assert(typeof url == 'string', 'tab.url should be a string');


    // busca texto selecionado no tab
    chrome.tabs.executeScript(tab.id, {
      code: "window.getSelection().toString();"
    }, function(result){

      // text selected
      if( (text = result[0]) != "" ){

        translate(text, function(translated){
          renderStatus(translated);
        });

      }
      else{
        renderStatus("Selecione um texto primeiro");
      }

    });

  });

}

function AdapterTranslatorYandexAPI(){
  this.key = "trnsl.1.1.20161108T115720Z.f6d7084fe1c74df1.936a31360efbe845cb5f648579a6f5d3087d5902";
  this.urlApi = "https://translate.yandex.net/api/v1.5/tr.json/translate?key={key}&text={q}&lang={s}&format=plain";

  this.translate = function(text, callback, options){

    var url = this.urlApi.replace("{key}", this.key)
      .replace("{q}", text)
      .replace("{s}", options.current_lang)
      .replace("{t}", options.for_lang);
    var method = "GET";
    
    var request = new XMLHttpRequest();
    request.responseType = "json";

    request.onreadystatechange = function(){

      if(this.readyState == 4 && this.status == 200){
        callback(request.response.text[0]);
      }
    }

    request.open(method, url, true);
    request.send();

  }

}

function AdapterTranslatorGoogleAPI(){

  this.key = "AIzaSyCgyX1BibB5ZSErfHS1Xi2ThN_VFS2B_mM";
  this.urlApi = "https://www.googleapis.com/language/translate/v2?key={key}&q={q}&source={s}&target={t}";

  this.translate = function(text, callback, options){
    // traduzir texto

    var url = this.urlApi.replace("{key}", this.key)
      .replace("{q}", text)
      .replace("{s}", options.current_lang)
      .replace("{t}", options.for_lang);
    var method = "get";

    
    var request = new XMLHttpRequest();
    request.responseType = "json";
    request.onreadystatechange = function(){

      // requisicao finalizada
      if(this.readyState == 4){
        callback({
          data: text_translated
        })

      }

    }
    request.open(method, url, true);
    request.send();
  }
}



function translator(className){

  var translator = null;

  switch(className){
    case "GoogleAPI": translator = new AdapterTranslatorGoogleAPI();
    break;
    case "YandexAPI": translator = new AdapterTranslatorYandexAPI();
    break;
  }

  return translator;
}



function translate(text, callback, options){

  var default_options =  {
    "current_lang": "pt", 
    "for_lang": "en"
  };


  translator("YandexAPI").translate(text, callback, default_options);
}


function renderStatus(statusText) {
  document.getElementById('status').innerHTML = statusText;
}

document.addEventListener('DOMContentLoaded', function() {
  getCurrentTabUrl();
});

Bom. Acho que é isso, abaixo o link para o github do projeto. Dêem um fork lá no projeto, escrevam bastante código, e mandem ideias, críticas, enfim, o que quiserem. Mandem também os pull-requests dos seus códigos para que possamos ir melhorando a extensão.

https://github.com/brunoom1/chrome-extension-translate

Abraço.


Voltar aos Posts