大多数人都会同意,国际化现有应用程序比从头开始开发国际化应用程序更昂贵。

这是真的吗?或者,当您从头开始编写一个国际化应用程序时,国际化的成本只是分摊到多个小任务上,而没有人觉得自己承担了国际化任务的全部重担?

您甚至可以声称,一个成熟的应用程序有很多很多 LOC,这些 LOC 在项目历史期间被删除,并且如果事后才进行国际化,则它们不需要 I18Ned,但如果项目国际化,则它们会是 I18N从一开始。

那么你认为今天开始的项目, 必须 是否国际化,或者是否可以根据软件是否成功以及需求的地理分布将这一决定推迟到未来。

我不是在谈论操纵 unicode 数据的能力。大多数主流语言、数据库和库都是免费的。我特别讨论的是支持您自己的软件的多种语言和区域设置的用户界面。

有帮助吗?

解决方案

“当您从头开始编写国际化应用程序时,I18N的成本是...摊销”

但是,这并不是整个故事。

追溯向用户追踪每条消息是不可能的。

不难。不可能的。

考虑一下。

theMessage = "Some initial part" + some_function() + "some following part";

您将有一个糟糕的时间找到所有此类情况。毕竟, some_function 只是返回一个字符串。您不知道它是数据库密钥(从未显示给人)还是必须翻译的消息。当翻译时,语法规则可能会表明,一个愚蠢的想法是一个愚蠢的想法。

您不能简单地将每个字符串值函数抓住,即包含必须翻译的I18N消息。您必须实际读取代码,并可能重写该函数。

显然,什么时候 some_function 根本没有任何复杂性,对于为什么您的应用程序的一部分仍处于瑞典语,而其余的I18N却成为其他语言,那么您感到困惑。 (不要特别选择瑞典人,将其替换为用于开发的任何语言与最终部署不同。)

更糟糕的是,当然,如果您在C或C ++工作,则可能在处理前宏和适当的C语言语法之间进行一些划分。

在动态语言中 - 可以随时建立代码 - 您将被设计瘫痪,在该设计中,您无法积极地识别所有代码。虽然动态生成代码是一个坏主意,但它也使您的追溯I18N作业不可能。

其他提示

我必须不同意,将其添加到现有应用程序中比从头开始使用新应用程序要多得多。

  • 在应用程序“大”之前,不需要很多时间I18N。当您确实变得庞大时,您可能会有一个更大的开发团队可以致力于I18N,因此这会减少负担。
  • 您实际上可能不需要它。当您没有需要它的客户时,许多小型团队都付出了巨大的努力来支持国际化。
  • 一旦国际化,它就会使逐步更改更加耗时。这并不需要很多额外的时间,但是每次您需要在产品中添加字符串时,都需要先添加到捆绑包中,然后添加参考。不,这不是很多工作,但这是努力,确实需要一些时间。

我更喜欢“到达它时越过那座桥”,并且只有在您有付费客户寻找它的情况下才国际化。

是的,将现有应用程序国际化绝对是 更昂贵 从第一天开始开发为国际化的应用程序。而且这几乎从来都不是微不足道的。

例如

Message = "Do you want to load the " & fileType() & " file?"

没有某些代码更改就无法国际化,因为许多语言都具有性别协议等语法规则。您通常需要一个不同的消息字符串来加载所有可能的文件类型,与英语不同的情况不同,可以将其插入子字符串。

许多其他问题: :您需要更多的UI空间,因为某些语言比英语需要更多的字符来表达相同的概念,您需要更大的字体来供东亚使用,您需要在用户界面中使用本地化日期/时间,但在与数据库进行通信时,您可能需要使用英语。需要将半隆作为CSV文件的分隔线,字符串比较和排序是文化,电话号码和地址...

因此,您认为从今天开始,必须国际化的项目,还是可以根据软件享受和需求的地理分布的成功(或不成功)推迟该决定?

这取决于. 。特定项目国际化的可能性有多大?快速获得第一个版本有多重要?

我不能说什么是昂贵的,但是我可以告诉您,干净的API可以让您以非常低的成本进行国际化的态度。

它看起来不像您为mietzins对象设置主键值。您需要在尝试保存到数据库之前设置主键值,或者告诉EF将由数据库生成主键。

如何指定键由数据库生成,取决于您是否首先使用代码或数据库。在代码中,默认情况下,默认为整数键类型,或者您可以使用databasegeneratedAttribute并设置标识。对于数据库,您可以使用EF Designer并将关键属性的Storegenered Facet设置为标识。

我使用以下类和代码首先映射在图表中重新创建了模型。 (我认识到您可能不是首先使用代码,但这是我创建相同模型的最简单方法,与图表中所示的所有关系。)

public class Mietzins
{
    public int Id { get; set; }
    public int LiegenschaftId { get; set; }
    public int MieterId { get; set; }
    public int ObjektId { get; set; }
    public DateTime StartDate { get; set; }
    public DateTime EndDate { get; set; }

    public Liegenschaft Liegenschaft { get; set; }
    public Mieter Mieter { get; set; }
    public Objekt Objekt { get; set; }
}

public class Mieter
{
    public int Id { get; set; }
    public int LiegenschaftId { get; set; }
    public int ObjektId { get; set; }
    public string Name { get; set; }

    public Liegenschaft Liegenschaft { get; set; }
    public ICollection<Mietzins> MietzinsMenge { get; set; }
    public Objekt Objekt { get; set; }
}

public class Objekt
{
    public int Id { get; set; }
    public int LiegenschaftId { get; set; }
    public int HausId { get; set; }
    public int Zimmer { get; set; }

    public Haus Haus { get; set; }
    public Liegenschaft Liegenschaft { get; set; }
    public ICollection<Mietzins> MietzinsMenge { get; set; }
    public ICollection<Mieter> MieterMenge { get; set; }
}

public class Liegenschaft
{
    public int Id { get; set; }
    public string Strasse { get; set; }
    public int UserId { get; set; }

    public ICollection<Haus> HausMenge { get; set; }
    public ICollection<Mieter> MieterMenge { get; set; }
    public ICollection<Mietzins> MietzinsMenge { get; set; }
    public ICollection<Objekt> ObjektMenge { get; set; }
}

public class Haus
{
    public int Id { get; set; }
    public int LiegenschaftId { get; set; }
    public int Nummer { get; set; }

    public ICollection<Objekt> ObjektMenge { get; set; }
    public Liegenschaft Liegenschaft { get; set; }
}
.

上下文:

public class ImmoReportsEntities : DbContext
{
    public DbSet<Liegenschaft> Liegenschaft { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Liegenschaft>()
            .HasMany(e => e.HausMenge)
            .WithRequired(e => e.Liegenschaft)
            .HasForeignKey(e => e.LiegenschaftId)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<Liegenschaft>()
            .HasMany(e => e.MieterMenge)
            .WithRequired(e => e.Liegenschaft)
            .HasForeignKey(e => e.LiegenschaftId)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<Liegenschaft>()
            .HasMany(e => e.MietzinsMenge)
            .WithRequired(e => e.Liegenschaft)
            .HasForeignKey(e => e.LiegenschaftId)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<Liegenschaft>()
            .HasMany(e => e.ObjektMenge)
            .WithRequired(e => e.Liegenschaft)
            .HasForeignKey(e => e.LiegenschaftId)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<Haus>()
            .HasMany(e => e.ObjektMenge)
            .WithRequired(e => e.Haus)
            .HasForeignKey(e => e.HausId)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<Objekt>()
            .HasMany(e => e.MietzinsMenge)
            .WithRequired(e => e.Objekt)
            .HasForeignKey(e => e.ObjektId)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<Objekt>()
            .HasMany(e => e.MieterMenge)
            .WithRequired(e => e.Objekt)
            .HasForeignKey(e => e.ObjektId)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<Mieter>()
            .HasMany(e => e.MietzinsMenge)
            .WithRequired(e => e.Mieter)
            .HasForeignKey(e => e.MieterId)
            .WillCascadeOnDelete(false);
    }
}
.

然后我完全像问题一样运行代码,它的工作正常。

我改变了mietzins的主键,以便它不再生成数据库:

public class Mietzins
{
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int Id { get; set; }
    public int LiegenschaftId { get; set; }
    public int MieterId { get; set; }
    public int ObjektId { get; set; }
    public DateTime StartDate { get; set; }
    public DateTime EndDate { get; set; }

    public Liegenschaft Liegenschaft { get; set; }
    public Mieter Mieter { get; set; }
    public Objekt Objekt { get; set; }
}
.

我再次运行代码并获取以下异常:

未处理的例外:system.data.entity.infrastructure.dbupdateException: 更新条目时发生错误。 有关详细信息,请参阅内部异常。 --- system.data.updateException:更新条目时发生错误。 有关详细信息,请参阅内部异常。 --- system.data.sqlclient.sqllexception:违反主键约束'pk_mietzins'。无法在对象'dbo.mietzins'中插入重复密钥。 该声明已被终止。

如果这与您看到的异常不同,那么请发布您的代码和完整的异常消息和堆栈跟踪,我将拍另一个外观。

I18N和L10N的概念比仅将字符串转换为一些语言要广泛。

示例:考虑用户日期和时间的输入。如果您在设计时还没有考虑国际化

a)用户的接口和

b)存储,检索和显示机制

当您想启用其他输入方案时,您将获得非常糟糕的时光。

同意,在大多数情况下,I18N首先不是必需的。但是,这就是我的意思,如果您不花钱 一些 该区域必须触及I18N,您会发现自己最终重写了原始代码的大部分。然后,添加I18N 比事先花了一些思考要贵得多。

似乎这可能是一个大问题的一件事是,以各种语言的消息的角色计数不同。我在iPhone应用程序上进行了一些工作,尤其是在小屏幕上,如果您为具有10个字符的消息设计UI,然后尝试国际化,并发现您需要20个字符才能显示您现在必须重做UI的相同内容适应。即使使用桌面应用程序,这仍然可能是一个大PITA。

这取决于您的项目以及您的团队的组织方式。

我参与过一个网站的国际化,由一名开发人员全职工作了一年,大概有 6-8 个月的兼职时间让我在需要时处理安装影响(重新组织文件等),以及其他工作当项目需要大量重构时,开发人员会不时参与其中。这是在 v3 版本的应用程序中。

所以这肯定很贵。您要问的是,从一开始就提供本地化系统的成本是多少,以及这对早期阶段的项目有何影响。您的 v1 项目可能无法承受因仓促设计的国际化框架问题而导致的延迟和挫折,而拥有广泛客户群的稳定 v3 项目可能有资本投资来正确地做到这一点。

它还取决于您是否想要国际化所有内容,包括日志消息,或者只是 UI 字符串,以及这些 UI 字符串有多少,以及谁可以进行本地化和随之而来的 QA,甚至是哪些语言您想要支持 - 例如,您的系统是否需要支持 unicode 字符串(这是亚洲语言的要求)。

也不要忘记,将数据库后端更改为支持国际数据也可能是昂贵的。只需尝试将该Varchar字段更改为NVARCHAR,当时您已经拥有20,000,000个记录。

我认为它 取决于语言. 。每个J2EE(Java Web)应用程序都是I18N,因为它非常简单(即使IDE也可以为您提取字符串,您只需命名它们)。

在j2ee中,稍后添加便宜, 但是,文化是要尽快添加它们。我认为这是因为J2EE使用了很多开源,几乎所有开源的液体都是I18N。这对他们来说是个好主意,但对于大多数J2EE应用程序而言,这不是。 大多数企业应用仅适用于一家会说一种语言的公司.

另外,如果你有 不良测试人员 把它放太早让他们给你 有关标签和翻译的错误报告(我只看到开发人员所做的翻译一次)。测试人员完成后,您拥有具有出色I18N支持的Buggy应用程序。但是,用户切换语言并查看是否可以使用它可能很有趣。但是,使用您的应用程序只是对他们来说很无聊的工作,所以他们甚至不会这样做。 I18N的唯一用户是测试人员.

怪异的弦线加入并不是J2EE文化,因为您知道有一天有人可能想让它成为I18N。唯一的问题是从HTML模板中提取标签。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top