如何国际化java源代码?

时间:2012-06-19 15:11:14

标签: eclipse internationalization antlr abstract-syntax-tree

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

我想将教程项目的源代码国际化(请注意,而不是运行时应用程序)。这是一个例子(在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建议的那些):

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

6 个答案:

答案 0 :(得分:2)

我会使用唯一的字符串作为方法名称(或者您想要被本地化版本替换的任何内容。

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 :(得分:2)

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

ANTLR不会做到这一点; AST是必要的,但还不够。请参阅Life After Parsing上的文章。 Eclipse&#34; AST&#34;如果Eclipse包提供对名称和类型解析的一些支持可能会更好;否则你永远无法弄清楚如何更换每个&#34; doSomething&#34; (可能是重载或本地),除非您愿意以相同的方式替换它们 all (并且您可能无法做到这一点,因为某些符号引用了Java库元素)。

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

基本上,您想要枚举标识符的所有注释,字符串和声明,将它们导出到外部数据库&#34;要映射(手动?由谷歌翻译?)到等效。在每种情况下,您不仅要注意感兴趣的项目,还要注意其精确位置(源文件,行,甚至列),因为在原始文本中拼写相同的项目可能需要在修改后的文本中使用不同的拼写。

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

如果您已捕获注释的解析器,则注释的枚举也很简单。 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)"

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

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

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

有趣的部分是更换标识符使用。为此,您需要了解两件事:

  • 对于标识符的任何使用,声明是什么用途;如果你知道这一点,你可以用声明的新名称替换它; DMS提供全名和类型分辨率以及使用列表,使这非常简单,
  • 在作用域中重命名的标识符是否与原始图像不同?一般来说这很难做到。但是,对于Java语言,我们有一个&#34;阴影&#34;检查,这样您至少可以在重命名后确定您有问题。 (甚至可以使用重命名程序来解决此类阴影冲突

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

鉴于你的目标,我有点惊讶你不想要一个源文件&#34; foo.java&#34;包含&#34;类foo {...}&#34;要重命名为.java。这不仅需要将转换后的树写入已翻译的文件名(非常简单),甚至可能需要重建目录树(DMS也提供了进行目录构建和文件复制的工具)。

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

答案 2 :(得分:1)

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

您可以使用Eclipse来帮助您。

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

答案 3 :(得分:0)

使用.properties文件,例如:

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

这样,Java会根据当前本地(从操作系统或Java语言环境设置中获取)选择Messages.properties文件。

该文件应位于类路径上,名为Messages.properties(默认值),或者为German等的Messages_de.properties。

有关完整教程,请参阅此内容: http://docs.oracle.com/javase/tutorial/i18n/intro/steps.html

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

如果您希望使用英语和其他语言的Javadoc,请参阅此SO thread

答案 4 :(得分:0)

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

的教程

Eclipse也具有这方面的功能(“外部化字符串”,我记得)。

对于函数名称,我认为没有任何结果,因为这将要求您在许多版本上维护代码源...

问候

答案 5 :(得分:0)

您可以使用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

然后在构建期间将模板与每个语言prop文件合并(使用Ant / Gradle / Maven等)。