【问题标题】:How do I internationalize catalogs?如何国际化目录?
【发布时间】:2015-07-13 15:32:42
【问题描述】:

我必须国际化一个项目,不仅是应用程序的标签,我还必须国际化目录,例如

国家:

  • ES :墨西哥、阿勒曼尼亚、英格拉特拉等地。
  • EN : 墨西哥、德国、英格兰等

我不想创建像下一个这样的模型:

CREATE TABLE country (
  id_country INT NOT NULL AUTO_INCREMENT,
  name_es VARCHAR(100) NOT NULL,
  name_en VARCHAR(100) NOT NULL,
  ....................,
  PRIMARY KEY (`id_country`));

是否有解决此问题的最佳实践、模式设计或框架?因为我认为这是一个常见问题。我正在考虑下一个模型:

CREATE TABLE country (
  id_country INT NOT NULL AUTO_INCREMENT,
  name VARCHAR(100) NOT NULL,
  languaje VARCHAR(100) NOT NULL,
  PRIMARY KEY (`id_country`));

问题是我将只有一个国家/地区的许多外键,因此查询会更加复杂。

我正在使用 Java、Spring boot、Spring 数据、JavaServer Faces、Primefaces。

我希望你能帮助我。

谢谢

【问题讨论】:

  • 我不清楚你的问题。你能澄清一下吗?您的问题是否与 Unicode 编码有关?另外,“问题是我将只为一个国家/地区提供许多外键,因此查询将变得更加复杂”是什么意思。你能举一些例子吗?您使用的是什么数据库管理系统?
  • 我的问题与目录有关,例如国家、城市、性别(男性、女性或男性和女性),或者您将来需要更改的任何目录。

标签: java sql spring primefaces internationalization


【解决方案1】:

老实说,我发现在 XML 中进行翻译最灵活,并且更好地结合到版本控制中。人类实践也更容易。

但是,数据库允许灵活使用、过滤、排序等。

可以有一些评估用途的东西:报告、完整性检查、统计。

table Texts
    id       int auto_incr -- internally
    key      varchar 30 -- public key in software
    comment  varchar 80 -- translator info, like noun, in menu
    categ    varchar 20 -- glossar entry / menu / help / tooltip
    sortkey  varchar 20 -- to order the translation
    ...

table Locales
    locale   varchar 10

table Translations
    textid   int
    locale   varchar 160

这个想法是准备协调为翻译工作。 还有翻译记忆格式导出/导入翻译服务。 词汇表对于具有一致的术语非常重要。 开发人员在翻译方面付出的努力提高了质量,并节省了所有工作。

生产数据库可以是摘录,甚至可以是生成的java ListResourceBundle: 字符串数组。

【讨论】:

  • 但是你如何使用xml来建立关系呢?想象下一个场景:你有以下实体:人、国家和爱好,你需要坚持一个人的出生地(国家)和它的所有爱好。您需要将所有应用程序国际化,包括国家和爱好。
  • 从数据库中获取文本代码,从用户上下文获取语言,键 = 代码 + 语言。以某种形式保存 XML 的文本翻译调用然后传递文本。 (当然这有点简单。)所有的优化都可以在 i18n 首次运行之后进行。
  • 由于我的回答是相同的评论,我将在这里写下我同意 Joop。
【解决方案2】:

如果按目录标题排序对您来说不是问题,最好的方法是将翻译保留在属性文件中,而不是表中的标题列,而是在表中添加一个键列并使用该键根据语言环境引用标题。这种方法的缺点是您不能根据标题对数据进行排序。好处是您不受要支持的语言数量的限制。

但是,如果数据库查询的排序、分组...特性很重要,那么您别无选择,只能使用字典表,就像其他答案中解释的那样。这种方法的缺点是查询性能较低,因为与字典表的连接太多,当然还有丢失键的风险,这会使您使用外部连接时性能更差!

字典表结构如下:

CREATE TABLE dictionary(
  id INT NOT NULL,
  locale VARCHAR(2) NOT NULL,
  value VARCHAR(100) NOT NULL,
  PRIMARY KEY (id, locale);

例如国家表将是这样的:

CREATE TABLE country (
  id_country INT NOT NULL AUTO_INCREMENT,
  name_dictionary_id INT NOT NULL,
  ....................,
  PRIMARY KEY (`id_country`));

按本地检索国家/地区列表的查询将是这样的:

select 
   c.id_country, d.value
from 
   country c
left outer join dictionary d
   on (c.name_dictionary_id = d.id and d.locale = :locale)
order by d.value asc

:locale 是一个参数,必须根据用户/浏览器请求的区域设置发送到查询。

当您创建新国家/地区时,您需要获取支持的语言环境的所有名称,并为每个语言插入一个字典,所有名称都具有相同的 ID,但语言环境不同。

【讨论】:

  • 是的,我知道如何使用属性文件,但这对静态内容很有用,我提到的问题与目录等动态内容有关,例如国家,想象一下我需要在我的应用程序的某些选择器中显示国家,我需要翻译并将它们保存在数据库中。
  • 所以字典表将是您更好的解决方案。我可以编辑答案并为您提供表格的完整描述和设计。
【解决方案3】:

是的,这是一个常见问题,并且有最佳做法。

对于不太可能更改的静态文本(例如国家/地区名称),最佳做法是使用客户端(而非数据库端)本地化。在这种情况下,您将 DB 条目视为键并从资源文件(或消息源,取决于您希望如何实现它)中读取翻译。 JSF 在这里帮不了多少,您必须创建自己的 bean 才能读取翻译后的名称。

对于动态文本(即经常更改或用户可以输入的内容),最佳做法是使用带有复合键的查找表:

CREATE TABLE translation (
  key VARCHAR(100) NOT NULL,
  languageid VARCHAR(20) NOT NULL,
  translation VARCHAR(100) NOT NULL,
  PRIMARY KEY (`key`, `languageid`));

再一次,您可以将原始条目视为键,但不建议这样做。实际上,即使是静态场景也不建议这样做 - 它会迫使您重新使用相同文本的翻译。问题是,翻译可能会根据上下文而有所不同......所以最好使用唯一的翻译键,即使这意味着不存在语言中性翻译之类的东西。

【讨论】:

  • 但在这种情况下,我将重复目标 n 次,例如México、Mexico 和 Mejico,它们每个都有不同的主键,所以如果在我的应用程序中我必须搜索住在墨西哥的人,我必须创建复杂的查询,不是吗?
  • JSF 在这里帮不了你太多,你必须创建自己的 bean 才能读取翻译后的名称 this is completely false
  • @Luiggi:请阅读您给我们的示例。专注于 Java 代码。你能看见它吗?还是我应该更清楚地表达这一点?
【解决方案4】:

首先,对不起我的英语水平。

在我的职业生涯中,我曾两次遇到过您的问题。

  • 第一次,我记得我们做了你不想要的模型。我们在每个数据表上都有文本来为每种语言翻译 N 个额外的列。
  • 第二次,我们开发了其他模型。我们有一个用于所有翻译 + 缓存系统的数据表(我们必须显示大量动态文本,并且我们需要提高性能)。

在这两种情况下,在开发之前我们就知道我们会使用哪些语言。第二个模型是这样的:

CREATE TABLE Translations (
    id_translation INT NOT NULL AUTO_INCREMENT,
    es_ES VARCHAR(200),
    en_UK VARCHAR(200),
    ...(more languages)
    PRIMARY KEY ('id_translation')
);

CREATE TABLE Countries (
    id_country INT NOT NULL AUTO_INCREMENT,
    id_translation INT NOT NULL,
    ...
    PRIMARY KEY ('id_country'),
    FOREIGN KEY ('id_translation')
);

使用此模型,您只有一个键 (id_country) 和此键的 N 翻译,其中 N 是 Translations 数据表中的语言列数。将来,如果您需要添加新语言,则必须在 Translations 数据表中添加新列。在您的查询中,您必须join Countries with Translations (id_translation) 和SELECT 选择语言列。 如果您使用 Entities (JPA),您将拥有一个实体 Translation 包含在一个实体 Country 中。使用此对象图,您可以创建一个辅助 JSF 组件来根据所选语言显示正确的文本。

附:如果您的应用程序将多次访问您的数据库以显示翻译,我建议您使用像 Ehcache、Memcached 或类似的缓存系统以提高性能

【讨论】:

  • 这个模型的问题是,如果你想支持一种新的语言,你需要修改你的数据库和你的应用程序,你的表将按列而不是按行增长,如果你为每一行支持多种语言您将获得所有翻译,如果您要不断发展您的应用程序,这是一个巨大的问题。
【解决方案5】:

正如之前提到的(如果我没看错的话,在另外两个答案中),最好是翻译部分不在数据库中,但是由于您希望所有内容都在数据库中,所以这是我的 5 美分. 在此处发布时,我仍然没有弄清楚如何正确格式化代码,对此感到抱歉。另外我会写一些伪SQL,但我希望它会清楚。

有3张桌子:

CREATE TABLE country (
  id_country INT NOT NULL AUTO_INCREMENT,
  //--optionally you could have name VARCHAR in default language
  PRIMARY KEY (`id_country`));

CREATE TABLE language(
  id_lang INT NOT NULL AUTO_INCREMENT,
  name VARCHAR(50),
  PRIMARY KEY (`id_lang`));

CREATE TABLE countryInLanguage(
  id_country REFERENCES country.id_country, //-- FK to country table
  id_lang REFERENCES language.id_lang, //-- FK to language table
  nameInLang VARCHAR(100),
  PRIMARY KEY (`id_country, id_lang`));

表 countryInLanguage 称为关联表,表示 N 对 N 关系。当然,您的查询必须访问两个表而不是一个但仅才能获取给定语言的国家/地区名称。我会说这并不复杂。至少它很容易扩展——以防您添加新国家或新语言。对于您将使用 id_country 的所有其他内容。

这里占用了一些冗余存储,例如有许多语言 n 元组,其中某些国家/地区名称以相同的方式写入,但不要过多...

您还可以在国家/地区表中设置国家/地区名称列,以默认语言保存国家/地区名称(默认最常用于特定应用程序、用户、公司等)。该死,您甚至可以根据用户选择的默认语言来更新这些默认值。

但是,我必须重申(我自己和其他人)翻译最好保存在人类可读的文件中。为什么?翻译您的应用程序的人通常不知道如何处理数据库以及编写 SQL 语句和查询,但如果他们有一个文本文件(可以是 XML、键值文件...),那么对他们来说就很容易了。

【讨论】:

  • 但是你如何使用xml来建立关系呢?想象下一个场景:你有以下实体:人、国家和爱好,你需要坚持一个人的出生地(国家)和它的所有爱好。您需要将所有应用程序国际化,包括国家和爱好。
  • 为什么翻译需要关系? IMO 这太多余了。只需为每种语言创建一个带有键值对的文件,并根据所选语言使用适当的语言文件。这就是很多软件的工作原理,例如 VLC videolan.org/developers/i18n/vlc-howto.html
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-04-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-12
  • 1970-01-01
相关资源
最近更新 更多