【问题标题】:How to internationalize java source code?如何国际化java源代码?
【发布时间】:2012-06-21 16:04:45
【问题描述】:

编辑:我完全重写了这个问题,因为我的前两个版本似乎不够清楚。 感谢迄今为止的建议。

我想国际化一个教程项目的源代码(请注意,不是运行时应用程序)。这是一个例子(Java):

/** A comment */
public String doSomething() {
  System.out.println("Something was done successfully");
}

用英语,然后有法语版本是这样的:

/** Un commentaire */
public String faitQuelqueChose() {
  System.out.println("Quelque chose a été fait avec succès.");
}

等等。然后在某处有一个属性文件之类的东西来使用常用工具编辑这些翻译,例如:

com.foo.class.comment1=A comment
com.foo.class.method1=doSomething
com.foo.class.string1=Something was done successfully

对于其他语言:

com.foo.class.comment1=Un commentaire
com.foo.class.method1=faitQuelqueChose
com.foo.class.string1=Quelque chose a été fait avec succès.

我正在尝试找到最简单、最有效和不显眼的方法,用最少的人工繁重的工作(除了明显地翻译实际文本)来做到这一点。最好在 Eclipse 下工作。例如,原始代码将用英语编写,然后外部化(到属性,最好保持原始源不变),翻译(人工)然后重新生成(作为单独的源文件/项目)。

我发现的一些小径(AlexS 建议的除外):

我只是很惊讶没有一个工具可以做到这一点。

【问题讨论】:

  • 开发者必须懂英语。整个标准 Java API 都是英文的,他们将使用的 99.9% 的外部库也是如此。如果他们不懂英语,他们最好尽快学习。在面向开发人员的教程中,我不会费心翻译 Java 代码。
  • 不要学习他们的坏习惯。每个程序员都必须能看懂英文代码
  • @AlexS:作为波兰人,我已经阅读了许多波兰语讲座、教程和教科书,我必须承认我非常讨厌翻译变量、类和方法名称。英文关键字看起来很奇怪。此外,没有办法坚持有效的语法规则,这更让人痛苦。我真的很感谢人们保持代码不变(你总是可以详细描述它,无论如何你应该做什么)。
  • 请不要把这变成一场徒劳的哲学辩论。如果我想将源代码翻译成不同的语言,这是我的权利,这就是我要问的如何有效地做到这一点。谢谢。

标签: eclipse internationalization antlr abstract-syntax-tree


【解决方案1】:

我会使用唯一的字符串作为方法名(或任何你想被本地化版本替换的东西。

public String m37hod_1() {
  System.out.println(m355a6e_1);
}

然后我会像这样为每种语言定义一个属性文件:

m37hod_1=doSomething
m355a6e_1="Something was done successfully"

然后我会编写一个小程序来解析源文件并替换字符串。所以一切都在日食之外。

或者我会使用 ant 任务 Replace 和属性文件,而不是独立的翻译程序。 类似的东西:

<replace 
    file="${src}/*.*"
    value="defaultvalue"
    propertyFile="${language}.properties">
  <replacefilter 
    token="m37hod_1" 
    property="m37hod_1"/>
  <replacefilter 
    token="m355a6e_1" 
    property="m355a6e_1"/>
</replace>

使用其中一种方法,您无需在教程中解释任何有关本地化的内容(除非您愿意),但可以专注于您真正的主题。

【讨论】:

  • +1 用于回答实际问题。注意:有意义的 ID 可能更容易理解。
  • 感谢您的建议。是的,这是我目前知道的唯一方法(我想也许使用某种 grep / shell 搜索和替换工具),但它使原始代码更难阅读。如果可能的话,我真的很想找到一些对用户更友好并插入 Eclipse 的东西。
  • 也许您实际上可以将您的名字保留原样,并且效果相同。我主要使用 1337 作为极客因素 ;)
  • @zeartist 我不知道 ant 是如何集成到 eclipse 中的,但是如果您使用 ant,我认为您只需在构建属性中指定语言并点击编译即可。当然,您还必须制作一个 source.jar。也许明天晚上我会有时间更深入地挖掘它......
  • @AlexS - 在 Eclipse 中,您可以右键单击 ant 构建文件并选择 Run As>Ant Build...
【解决方案2】:

你想要的是一个庞大的代码更改引擎。

ANTLR 无法解决问题; AST 是必要的,但还不够。请参阅我在Life After Parsing 上的文章。如果 Eclipse 包对名称和类型解析提供一些支持,Eclipse 的“AST”可能会更好;否则你将永远无法弄清楚如何替换每个“doSomething”(可能是重载的或本地的),除非你愿意all相同地替换它们(你可能不能这样做, 因为有些符号是指 Java 库元素)。

我们的DMS Software Reengineering Toolkit 可用于完成您的任务。 DMS 可以将 Java 解析为 AST(包括注释捕获),以任意方式遍历 AST,分析/更改 AST,并将修改后的 AST 导出为有效源代码(包括 cmets)。

基本上,您希望枚举所有 cmets、字符串和标识符声明,将它们导出到外部“数据库”以映射(手动?通过 Google 翻译?)到等价物。在每种情况下,您不仅要注意感兴趣的项目,还要注意其精确位置(源文件、行、甚至列),因为在原始文本中拼写相同的项目在修改后的文本中可能需要不同的拼写。

如果你有 AST,枚举字符串就很容易了;只需爬取树并查找包含字符串文字的树节点。 (ANTLR 和 Eclipse 肯定也可以做到这一点)。

如果您拥有的解析器捕获了 cmets,那么枚举 cmets 也很简单。 DMS 可以。我不太确定是 ANTLR 的 Java 语法还是 Eclipse AST 引擎;我怀疑他们都有能力。

声明(类、方法、字段、局部变量)的枚举相对简单;有更多的情况需要担心(例如,匿名类包含对基类的扩展)。您可以编写一个过程来遍历 AST 并匹配树结构,但这是 DMS 开始发挥作用的地方:您可以编写看起来像您想要匹配的源代码的表面语法模式。例如:

   pattern local_for_loop_index(i: IDENTIFIER, t: type, e: expression, e2: expression, e3:expression): for_loop_header
         = "for (\t \i = \e,\e2,\e3)"

将匹配本地 for 循环变量的声明,并返回 IDENTIFIER、类型和各种表达式的子树;您只想捕获标识符(及其位置,可以通过从 DMS 在每个树节点上标记的源位置信息中获取 if 轻松完成)。您可能需要 10-20 个这样的模式来涵盖所有不同类型标识符的情况。

捕获步骤已完成,需要将所有捕获的实体翻译成您的目标语言。我把它留给你;剩下的就是把翻译后的实体放回去。

关键是精确的源位置。行号在实践中不够好;您可能在同一行中有多个翻译的实体,在最坏的情况下,一些具有不同的范围(例如,想象嵌套的 for 循环)。 cmets、字符串和声明的替换过程很简单;重新扫描树中与任何已识别位置匹配的节点,并用其翻译替换在那里找到的实体。 (您可以使用 DMS 和 ANTLR 执行此操作。我认为 Eclipse ADT 需要您生成一个“补丁”,但我想这会起作用。)。

有趣的部分在于替换标识符uses。为此,您需要了解两件事:

  • 对于标识符的任何use,声明的用途是什么;如果您知道这一点,则可以将其替换为声明的新名称; DMS 提供全名和类型解析以及使用列表,让这变得非常简单,并且
  • 重命名的标识符在作用域中的相互影响是否与原始标识符不同?一般来说,这更难做到。但是,对于 Java 语言,我们有一个“阴影”检查,因此您至少可以在重命名后确定您有问题。 (甚至还有一个重命名程序可用于解决此类阴影冲突

修补树后,您只需使用 DMS 的内置漂亮打印机将修补的树重写为源文件。我认为 Eclipse AST 可以写出它的树和补丁。我不确定 ANTLR 是否提供了从 AST 重新生成源代码的任何工具,尽管有人可能已经为 Java 语法编写了一个工具。这是harder to do than it sounds,因为所有的细节都很挑剔。 YMMV。

鉴于您的目标,我有点惊讶您不希望将包含“class foo { ... }”的源文件“foo.java”重命名为 .java。这不仅需要将转换后的树写入转换后的文件名(非常简单),甚至可能需要重建目录树(DMS 还提供了用于进行目录构建和文件复制的工具)。

如果您想为多种语言执行此操作,您需要为每种语言运行一次该过程。如果您只想为字符串执行此操作(经典的国际化案例),您将通过调用具有唯一资源 ID 的资源访问来替换每个字符串(需要更改,并非所有字符串都需要更改);运行时表将保存各种字符串。

【讨论】:

  • 谢谢,你提出了很多有用的信息。确实,解决方案不仅仅是解析,而是转换。我想我会选择一个简单的查找替换解决方案,因为听起来我可能会对这些解析器翻译器感到困惑,而我的用例是相当简单的东西。我将在下面发布我的官方答案。
【解决方案3】:

一种方法是用一种语言完成代码,然后翻译成其他语言。

您可以使用 Eclipse 来帮助您。

  1. 将完成的代码复制到特定语言的项目中。
  2. 然后:
    • 标识符: 在大纲视图(窗口>显示视图>大纲)中,选择每个项目并重构>重命名(Alt+Shift+R)。这负责重命名标识符,无论它在哪里使用。
    • 评论:使用搜索>文件查找“/*”或“//”的所有实例。单击每个并进行修改。
    • 字符串
      1. 使用 Source>Externalize 字符串查找所有文字字符串。
      2. 搜索>“Messages.getString()”文件。
      3. 点击每个结果并进行修改。
      4. 在每个文件上,“编辑>查找/替换”,将“//\$NON-NLS-.*\$”替换为空字符串。

【讨论】:

  • 谢谢,是的,这是一种方法。我想找到一种更自动化和更优雅的方式来做到这一点。前任。收集所有要外部化的字符串,使用工具生成一个大的属性文件,其中包含所有类、方法、变量、cmets 和带有某种限定标识符(例如 com.foo.class1.method1=myMethod 等)的字符串,准备好被翻译。
  • 明白。如果不存在这样的工具,请考虑这一点。 :) 根据我自己的本地化经验提供了这个答案,并且猜测教程的示例代码不太可能是大规模的。
【解决方案4】:

对于打印/记录的字符串,java 拥有一些国际化功能,即 ResourceBundle。有关于这个的教程on oracle site

Eclipse 也为此提供了功能(我记得是“外部化字符串”)。

对于函数名,我不认为有什么出处,因为这将需要您维护许多版本的代码源...

问候

【讨论】:

  • 谢谢,但这并不能回答问题。
【解决方案5】:

使用 .properties 文件,例如:

Locale locale = new Locale(language, country);
ResourceBundle  captions= ResourceBundle.getBundle("Messages",locale);

这样,Java 会根据当前本地(从操作系统或 Java 区域设置获取)选择 Messages.properties 文件

该文件应位于类路径中,称为 Messages.properties(默认文件)或 Messages_de.properties(德语等)。

完整教程请参见: http://docs.oracle.com/javase/tutorial/i18n/intro/steps.html

就源代码而言,我强烈建议使用英语。像 getUnternehmen() 这样的方法名称对于普通开发人员来说比普通的英文名称更糟糕。 如果您需要让外国开发人员熟悉您的代码,请用他们的语言编写适当的开发人员文档。

如果您想要英语和其他语言的 Javadoc,请参阅SO thread

【讨论】:

  • 这不能回答我的问题,抱歉。如果需要,请重新阅读。
【解决方案6】:

您可以使用freemarker 模板(或其他模板语言,例如velocity)编写代码。

doSomething.tml

/** ${lang['doSomething.comment']} */
public String ${lang['doSomething.methodName']}() {
    System.out.println("${lang['doSomething.message']}");
}

lang_en.prop

doSomething.comment=A comment
doSomething.methodName=doSomething
doSomething.message=Something was done successfully

然后在构建过程中将模板与每个语言属性文件合并(使用 Ant / Gradle / Maven 等)

【讨论】:

    猜你喜欢
    • 2021-05-17
    • 2011-03-13
    • 1970-01-01
    • 1970-01-01
    • 2013-06-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多