7

如果您想在全球范围内销售,则必须拥有一个完全国际化的应用程序。在 Java 中,我们使用资源包来解决静态文本代码方面的问题。

但是您如何处理存储在数据库中的文本?从静态定义开始,到用户可修改的对象,以用户输入的数据结束。

假设您有一个由具有不同区域设置的用户使用的数据库 - 您如何处理这个问题?您的国际化程度如何?你在哪里画线?什么解决方法可以阻止用户接收他们不懂的语言的文本?

4

7 回答 7

6

不要将系统生成的文本存储在数据库中。相反,存储一个代码(如消息编号),然后在 GUI 级别将其国际化。确保直接来自数据库的唯一文本是用户自己输入的文本。确保您的数据库设置为接受 unicode 文本。

于 2008-11-24T13:27:20.690 回答
2

首先,要非常清楚这些限制。对于用户创建的内容,如果您想本地化用户输入到您的应用程序中的内容,您正在查看社区翻译(不稳定)、机器翻译(不可靠)或付费人工翻译(昂贵!)。您可能想要求您的用户提供两个版本 - 一个用于您的默认文化(英语?),另一个用于他们的本地化文化,以便您可以为其他用户提供备用翻译?

其次,为一些极其冗长的数据库迁移做好准备……如果您在 Excel 电子表格中有四列文本,那么您突然需要将每个值插入翻译系统,检索本地化 ID,然后将其存储起来在您实际导入的表中 - 并且SELECT *只会为您提供短语 ID,您需要通过根据翻译表对它们进行本地化来将其解析回字符串。

也就是说 - 您可以本地化由典型项目中的数据库驱动的许多查找表、下拉列表等。其他评论已经提到在数据库中存储引用外部资源文件或电子表格的 StringId 值,但如果您有兴趣将所有本地化文本与数据本身一起保存在数据库中,那么您可能会发现这种方法很有用。

我们使用了一个名为 Phrase 的表,其中包含应用程序中每段文本的 ID 和默认(英文)内容。

您的其他表格最终看起来像这样:

CREATE TABLE ProductType (
    Id int primary key,
    NamePhraseId int, -- link to the Phrase containing the name of this product type.
    DescriptionPhraseId int
)

创建第二个表 Culture,其中包含您支持的特定和中性文化。对于奖励积分,将此表实现为自引用树(每个文化记录包含一个可为空的 ParentCultureCode 引用),因此您可以从特定文化(加拿大法语的“fr-CA”)回退到中性文化(“fr”如果不存在区域本地化),到您的不变/默认文化(通常是“en”,因为它被广泛使用)

您的实际翻译位于 LocalizedPhrase 表中,如下所示:

CREATE TABLE LocalizedPhrase (
  PhraseId int primary key,
  CultureCode varchar(8) primary key,
  Content nvarchar(255) -- the actual localized content
)

如果您想提供男性/女性特定的本地化,您可以扩展此模型:

CREATE TABLE GenderedLocalizedPhrase (
  PhraseId  int primary key,
  CultureCode varchar(8) primary key,
  GenderCode char(1) primary key, -- 'm', 'f' or '?' - links to Gender table
  Content nvarchar(255)
)

您将希望将整个表图形缓存在内存中并相应地修改查询/连接策略 - 缓存 Phrase 类中的本地化并覆盖 Phrase 对象上的 ToString() 方法以检查当前线程文化是一种方法。如果您尝试在查询中执行此操作,则会产生巨大的性能成本,并且每个查询最终都会如下所示:

-- assume @MyCulture contains the culture code ('ca-FR') that we are looking for:
SELECT 
    Product.Id, 
    Product.Name, 

    COALESCE(ProductStatusLocalizedPhrase.Content, ProductStatusPhrase.Content) as ProductStatus, 
    COALESCE(ProductTypeLocalizedPhrase.Content, ProductTypePhrase.Content) as ProductType, 
  FROM Product

    INNER JOIN ProductStatus ON Product.StatusId = ProductStatus.Id
    INNER JOIN Phrase as ProductStatusPhrase ON ProductStatus.NamePhraseId = Phrase.Id
    LEFT JOIN LocalizedPhrase as ProductStatusLocalizedPhrase 
      ON ProductStatus.NamePhraseId = ProductStatusLocalizedPhrase.Id and CultureCode = @MyCulture

    INNER JOIN ProductType ON Product.TypeId = ProductType.Id
    INNER JOIN Phrase as ProductTypePhrase ON ProductType.NamePhraseId = Phrase.Id
    LEFT JOIN LocalizedPhrase as ProductTypeLocalizedPhrase 
      ON ProductType.NamePhraseId = ProductTypeLocalizedPhrase.Id and CultureCode = @MyCulture
于 2008-11-24T14:19:15.077 回答
1

我们将数据库中的许多文本更改为“key:default text”,然后在我们的翻译文件中查找“key”。这涵盖了客户在数据库中没有更改的所有文本(例如,所谓的“信用票据”)。当客户确实更改了文本时,他们只需删除密钥,这样他们就总能获得价值。

我们的系统有一些表格,其中包含需要上述配置数据的表格,如果每个客户只需要一种语言,则只包含客户输入的文本的表格不是问题。

于 2009-06-02T11:57:08.487 回答
1

什么解决方法可以阻止用户接收他们不懂的语言的文本?

这只是用户输入数据的问题。因此,如果您想避免其他用户看到他们可能不理解的语言的内容,请将语言环境代码与内容一起存储,并且只向具有相同语言环境/用户选择的语言的任何人显示该内容。

另一方面,用户可能知道几种语言,因此我不会限制他们查看内容,我只会添加一条通知,例如“此内容无法以您选择的语言提供,...”,然后将内容显示在可用的语言。通过这种方式,您可以增加用户获得她可以理解的内容的可能性。

于 2008-11-24T13:42:41.750 回答
1

假设您有一张桌子:

create table countries (
  country_id int primary key,
  short_name text not null unique,
  official_name text not null unique,
  iso_code char(2) not null unique
);

insert into countries values (12, 'Algeria', 'The People''s Democratic Republic of Algeria' 'DZ');

然后创建一个转换表:

create table countries_t (
  country_id int not null references countries(country_id),
  short_name text not null,
  official_name text not null,
  locale varchar(5) not null,

  primary key (country_id, locale)
);

insert into countries_t values
(12, 'Algérie', 'la République algérienne démocratique et populaire', 'fr'),
(12, 'Algerien', 'Demokratische Volksrepublik Algerien', 'de-DE');

创建一个视图以根据用户定义的会话变量返回数据。以下是特定于 PostgreSQL 的,但您的数据库可能支持自定义会话变量,否则使用临时表:

create view countries_i18n as 
  select 
    a.country_id,
    coalesce(c.short_name, b.short_name, a.short_name) as short_name, --default to countries.name if translation not found
    coalesce(c.official_name, b.official_name, a.official_name) as official_name
    countries.iso_code
  from countries a
  left join countries_t b on b.id = a.id and b.locale = current_setting('my_custom_guc.locale')
  left join countries_t c on c.id = a.id and c.locale = left(current_setting('my_custom_guc.locale'), 2); --fall-back to 2-letter locale

用德语查询德语表:

SET my_custom_guc.language_code = 'de-DE';

select country_id, iso_code, short_name, official_name from countries_i18n;

country_id  iso_code  short_name  official_name
-----------------------------------------------
12          DZ        Algerien    Demokratische ...

用加拿大法语查询表(回退到通用法语):

SET my_custom_guc.locale= 'fr-CA';

select country_id, iso_code, short_name, official_name from countries_i18n;

country_id  iso_code  short_name  official_name
-----------------------------------------------
12          DZ        Algérie     la République ...

用西班牙语查询表格(没有翻译,返回英文):

SET my_custom_guc.language_code = 'es';

select country_id, iso_code, short_name, official_name from countries_i18n;

country_id  iso_code  short_name  official_name
-----------------------------------------------
12          DZ        Algeria    The People's D ...
于 2014-02-22T01:01:48.790 回答
0

静态数据是最简单的,我会创建一个翻译表,所以想象一个 UserStatus 表,它有一个 StatusId、TranslationToken,然后 TranslationTable 有一个令牌、语言和文本。

或者类似地,您可以只返回令牌以供应用程序使用您的资源文件进行处理。

至于用户输入数据,这要复杂得多。您至少需要接受 unicode 字符,然后问题就变成了排序和比较。排序是最大的一项。您可以做的很多事情取决于您的应用程序。因此,如果您的数据库在任何时候都只需要支持一种语言(想象一下,如果您的应用程序分发给您的客户),那么排序规则就是一个有争议的问题,因为您可以在安装时设置它。

但是,如果您必须在单个数据库中支持多种语言,则需要正确处理排序规则。我们发现动态更改排序规则的唯一方法是在查询中设置它,这需要生成动态 sql。这个例子是你将俄语、英语和波兰语都存储在同一张表的一个字段中。

我们从未探索过拉丁语和西里尔语排序规则之外的任何东西,但我想亚洲语言也会如此。

于 2008-11-24T13:32:33.160 回答
0

我们为我们的系统使用 XML 文件。该文件包含与我们模块的特定部分的关键关联。这样我们就可以快速地做XPath 来检索信息。每种语言都有 1 个文件(我们目前支持 2 种语言,但添加语言非常简单,只需复制粘贴文件即可)。这个解决方案并不完美,但有一些优点:

  1. 不在数据库中。
  2. 可以由编程之外的人编辑。
  3. 易于在多个视图中实现(我们有 WinForm 和 WebForm)。
于 2008-11-24T13:42:40.127 回答