segunda-feira, 11 de agosto de 2014

Mecanismo de Globalização de Base de Dados

Introdução

Hoje em dia a maioria das aplicações que utilizamos (principalmente as baseadas na Internet) permitem a apresentação do seu user interface (UI) em várias línguas. Esta é uma das funcionalidades de internacionalização e localização de aplicações.

Também é comum as aplicações terem conteúdos dinâmicos (como opções de menu, links, listas de opções, etc.), que podem estar guardados numa Base de Dados (BD). Se a aplicação for globalizada, então é necessário termos os conteúdos provenientes da BD em várias línguas também.

Neste artigo vamos analisar um mecanismo que permite guardar conteúdos em múltiplas línguas, numa BD e fazer a sua leitura de uma forma consistente e automática, na língua pretendida.

Scripts: todos os scripts com os exemplos deste artigo podem ser encontrados aqui.

Conteúdo

Compreender o problema

Num mundo globalizado como o actual, é essencial desenvolver aplicações globalizadas, i.e. que possuam mecanismos automáticos de internacionalização e localização.

Neste contexto, entende-se globalização como a capacidade das aplicações apresentarem mensagens em diferentes línguas e culturas (moeda, formato números, unidades medida, etc.), dependendo da localização do utilizador.

As diversas tecnologias e frameworks de programação de software têm apresentado, ao longo do tempo, mecanismos de globalização que permitem, com relativa facilidade, apresentar mensagens localizadas, conforme o objectivo mencionado no parágrafo anterior. Tipicamente, os textos das mensagens são colocados em ficheiros de resources separados por cada Língua/Cultura (p.ex. pt-PT=Português de Portugal ou pt-BR= Português do Brasil), que são chamados automaticamente, conforme a cultura activa.

Estes mecanismos funcionam para textos que estejam embebidos no próprio código fonte das aplicações. Todavia, é comum as aplicações terem textos e conteúdos dinâmicos  guardados em BD. Vejamos dois exemplos:

  • Uma dada aplicação tem menus dinâmicos, cujas opções dependem da licença de software e do perfil de cada utilizador. As várias opções dos menus (etiqueta a apresentar, perfil de acesso, recursos, etc.) estão guardadas na BD.
  • Numa dada aplicação é pedido ao utilizador que preencha os seus dados pessoais, existindo alguns campos em que tem que escolher um valor duma lista, como p.ex. estado civil, regime casamento, género, tipo de documento identificação, habilitações literárias, etc. Estas listas de valores estão guardadas em tabelas na BD.

No primeiro caso temos as etiquetas das opções dos menus que necessitam ser globalizadas, no segundo caso, temos dados de referência (as listas de valores) que necessitam ser globalizadas, também.

A questão que se coloca é como fazer a globalização destes dados, guardados em BD.

Visão geral

Com este mecanismo, a BD mantém a estrutura das tabelas originais, com os dados de negócio. Estas tabelas vão conter os dados no idioma e cultura neutro ou por defeito (para o presente artigo, vamos considerar que é pt-PT).

Vai ser criada uma tabela adicional, que regista as informações localizadas para os campos das outras tabelas, que necessitem tradução.

Como alternativa a ter uma única tabela com a informação localizada para TODAS as outras tabelas, esta pode ser dividida por várias tabelas, dependendo, por exemplo, da área funcional. Em qualquer dos casos, deve sempre ser muito claro qual a tabela de localização a utilizar.

As principais vantagens deste método são as seguintes:

  • Tem pouco impacto sobre o desenvolvimento da aplicação, porque toda a selecção da localização pode ser realizada na BD. A única questão é que todas as consultas à BD devem ser realizadas por meio de uma Stored Procedure (SP), com o código de cultura como parâmetro;
  • O método de localização permite utilizar valores predefinidos ou por defeito, que geralmente são os valores da tabela principal;
  • Apenas os dados localizados são registados, nenhum dado desnecessário é replicado;
  • É muito fácil mudar para uma abordagem não globalizada. Consultam-se simplesmente os dados das tabelas principais e não se utiliza a tabela localizada.

Objectos de Base de Dados utilizados pelo Mecanismo

Este método pode ser aplicado a qualquer tido de BD. Para efeitos da descrição e dos exemplos apresentados neste artigo, vamos utilizar o Microsoft SQL Server.

Objecto Tipo Descrição
CultureInfo Tabela
Esta tabela irá registar as informações de referência para os idiomas e culturas disponíveis no sistema.
Todos os dados localizados devem ter uma chave externa com o identificador de cultura obtido desta tabela.
ObjectGlobalization Tabela
Esta tabela irá registar a informação localizada para todas as tabelas na base de dados, utilizando um modelo EAV (Modelo Entidade Atributo Valor).
Cada linha tem uma chave que identifica a tabela, a coluna, a cultura e o Id da linha para os dados localizados.
GetGlobalizedValue Função
Esta é uma função auxiliar que permite ler o valor localizado a partir da tabela anterior, utilizando os campos-chave como parâmetros.
GetGlobalizedTextValue Função
Esta função é uma variação da função anterior que lê apenas valores de texto (nvarchar).
usp_SaveGlobalizedValue SP
Esta é uma SP auxiliar, para guardar os dados localizados na tabela ObjectGlobalization

Tabela CultureInfo

CultureName DisplayName ISO_639x_Value Culturecode
af-ZA Afrikaans - South Africa 0x0436 AFK
ar-AE Arabic - United Arab Emirates 0x3801 ARU
ar-BH Árabe - Bahrain 0x3C01 ARH
ar-DZ Árabe - Algeria 0x1401 ARG

O conteúdo da tabela CultureInfo é trivial.

Tabela ObjectLocalization

ObjectName ColumnName Culture RowId Value
dbo.PortType Name pt-PT 1 Porto Marítimo
dbo.PortType Name pt-PT 2 Porto Seco
GLOBAL_VALUE Description en-US 0 No
GLOBAL_VALUE Description en-US 1 Yes
GLOBAL_VALUE Description en-US All All
GLOBAL_VALUE Description pt-PT 0 Não
GLOBAL_VALUE Description pt-PT 1 Sim
GLOBAL_VALUE Description pt-PT All Todos

Coluna Tipo Descrição
ObjectName Varchar(128)
Normalmente esta coluna regista o Nome de tabela.
Para casos especiais, pode utilizar um nome de convenção, como GLOBAL_VALUE
ColumnName Varchar(128) Este é o nome da coluna que deseja localizar
Culture Varchar(10) Esta é a chave externa para a tabela CultureInfo
RowId Sql_Variant
Regista o valor na coluna de Id da linha na tabela original. Este valor de Id pode ser qualquer tipo de dados do SQL.
Value Sql_Variant Esta coluna regista o valor localizado
ValueText Varchar(max)
Esta coluna regista o valor de texto localizado. Ela pode ser utilizada como uma alternativa à coluna de valor
ValueXML XML
Esta coluna regista o valor localizado XML. Ela pode ser utilizada como uma alternativa à coluna de valor também

Sempre que for necessário criar um valor localizado, basta inserir uma nova linha na tabela ObjectGlobalization com o nome da tabela, o nome da coluna e o valor de Id para identificar a linha e o valor localizado. Existe uma SP para executar esta tarefa.

Function GetGlobalizedValue

Parâmetro Tipo Descrição
@ObjectName Varchar(128)
O nome do objecto a pesquisar na tabela ObjectGlobalization
@ColumnName Varchar(128) O nome da coluna a pesquisar na tabela ObjectGlobalization
@Culture Varchar(10) O nome da cultura a pesquisar na tabela ObjectGlobalization
@RowId Sql_Variant O Id da linha a pesquisar na tabela ObjectGlobalization
@Default Sql_Variant
Este é o valor por defeito, opcional, para a função, se a consulta sobre a tabela ObjectGlobalization não retornar dados. É tipicamente o valor da tabela original (cultura predefinida)

Exemplos de utilização:

SELECT GetGlobalizedValue('PortType', 'Name', 'fr-FR', PortTypeId, PortTypeName)
FROM PortType

Este exemplo lê o valor localizado, em Francês, da coluna Name da tabela PortType, utilizando o valor da coluna PortTypeName original como valor predefinido.

SET @label=GetGlobalizedValueText('GLOBAL_VALUE', 'Name', 'pt-PT', 'All', 'All')

Este exemplo define a variável @label para o valor localizado em Português da string 'All' com o valor em Inglês predefinido.

Neste caso, o nome de objecto, nome de coluna e identificador de linha são convenções, uma vez que não é um valor de uma tabela.

Function GetGlobalizedValueText

Faz o mesmo que a função anterior, com a única diferença de que o valor retornado é obtido a partir da coluna ValueText na tabela ObjectGlobalization.

Stored Procudure usp_SaveGlobalizedValue

Faz o mesmo que a função anterior, com a única diferença de que o valor retornado é obtido a partir da coluna ValueText na tabela ObjectGlobalization.

Parâmetro Tipo Descrição
@ObjectName Varchar(128) O nome do objecto a registar na tabela ObjectGlobalization
@ColumnName Varchar(128) O nome da coluna a registar na tabela ObjectGlobalization
@Culture Varchar(10) O nome da cultura a registar na tabela ObjectGlobalization
@RowId Sql_Variant O Id da linha a registar na tabela ObjectGlobalization
@Value Sql_Variant
O valor localizado para coluna especificada a registar na tabela ObjectGlobalization
@ValueText Varchar(max)
Um valor de texto localizado alternativo, para a coluna especificada, a registar na tabela ObjectGlobalization. Em casos práticos, este será o valor usado principalmente.
@ValueXml XML
Um valor XML localizado alternativo, para a coluna especificada, a registar na tabela ObjectGlobalization
@username Varchar(10)
Um parâmetro opcional para registar o nome do utilizador responsável pelo registo da informação localizada. Se este parâmetro não for especificado, o utilizador do sistema será registado.

Quando for necessário criar ou actualizar um registo na tabela ObjectGlobalization deve-se utilizar esta SP.

Exemplos de utilização:

EXEC    [dbo].[SaveGlobalizedValue]
        @ObjectName = 'PortType',
        @ColumnName = 'Name',
        @Culture     = 'fr-FR',
        @RowID     = 333,
        @Value     = NULL,
        @ValueText     = 'Port au Prince',
        @ValueXml     = NULL,
        @username     = 'kzinga'

Este exemplo regista (ou actualiza) a informação localizada para um dos valores em Francês que seriam lidos no exemplo anterior na função GetGlobalizedValue.

Considerações alternativas

Existem vários métodos de localização de BD, que podem ser tipificados nas secções que se seguem.

Embora cada um destes métodos apresente as suas vantagens, existem algumas desvantagens significativas em comparação com o método de Globalização de Objectos apresentado anteriormente.

Base de Dados Clone

Utilizando este método cria-se um clone completo da BD. O resultado é uma cópia exacta da estrutura original, incluindo todas as tabelas e campos. As BDs diferem apenas no nome e os conteúdos em diferentes idiomas.

Este método tem as desvantagens de exigir uma gestão complexa de conexões e uma duplicação desnecessária de dados (de todos os campos não localizados).

Field Database Localization

Utilizando este método, os valores dos campos localizados são criados na mesma linha da tabela. Os campos localizados são iguais ao campo original, com a excepção de conterem dados num idioma diferente e, é claro, os seus nomes diferem.

A principal desvantagem consiste no facto de que o código deve escolher dinamicamente a coluna com base no idioma selecionado e, claro, parece uma boa solução quando se começa com 2 idiomas, mas se o sistema cresce e precisar de um 3 º idioma? E um 4º? …

Linha Clone

Cada linha da tabela original é copiada para cada idioma. As linhas copiadas são iguais ao original, com a excepção de o valor do campo de idioma ser diferente. O resultado pode ter esta visualização:

As principais desvantagens são que, embora tenha pequeno impacto sobre o desenvolvimento da aplicação (o idioma é usado como chave para selecionar a linha e não na coluna) o conteúdo da tabela vai crescer e vai aumentar a complexidade de administrá-la.

Tabela Clone

Este método adiciona tabelas localizadas para as tabelas que se deseja localizar. As tabelas localizadas contêm somente a chave primária e os campos que são localizados. O resultado é escrito nas novas tabelas com a mesma estrutura. As tabelas de base de dados só têm nomes diferentes, como Descricao e Descricao_ja:

Outra opção para este método é ter a tabela original com todos os campos e a tabela localizada apenas com códigos de idioma e os campos localizados. É uma espécie do exemplo de Linha Clone, mas com as linhas localizadas numa tabela separada.

Tal como o método de localização de campo, a principal desvantagem é que o código deve escolher dinamicamente o nome de tabela com base no idioma selecionado. E se o número de idiomas utilizados pelo sistema começar a crescer, acabará com um número excessivo de tabelas.

Referências

http://www.sisulizer.com/localization/software/server-desktop-database.shtml

http://www.sqlservercentral.com/Forums/Topic205432-230-1.aspx

Sem comentários:

Enviar um comentário