[Eclipse]GEF入门系列(序)

file

前些天换了新电脑,本人一直处于兴奋中,基本是"不务正业"的状态。快过年了,虽然没什么动力干活,但我玩游戏技术比较差,魔兽3打电脑一家还很费劲,干脆写写帖子就当是休息吧!

由于工作的需要,最近开始研究GEF(Graphical Editor Framework)这个框架,它可以用来给用户提供图形化编辑模型的功能,从而提升用户体验,典型的应用如图形化的流程设计器、UML类图编辑器等等。其实一年多来我们做的项目都是和它有关的,只是之前我具体负责的事情和它没什么关系。那时也看过黄老大写的代码,EMF和GEF混在一起特别晕,没能坚持看下去。这次自己要动手做了,正好趁此机会把它搞明白,感觉GEF做出来的东西给人很专业的感觉,功能也很强大,应该挺有前途的。此外,GEF里用到了很多经典模式,最突出的如大量应用Command模式,方便的实现Undo/Redo功能等等,通过学习GEF,等于演练了这些模式,比只是看看书写几个类那种学习方式的效果好很多。

现在网上关于GEF的文章和教程还不是很多(比起一年前还是增加了几篇),基本上都是eclipse.org上的那些,其中少数几篇有中文版,中文的原创就属于凤毛麟角了,市场上似乎也没有这方面的成书。GEF SDK里自带的文档则比较抽象,不适合入门。我觉得最好的入门方法是结合具体的例子,一边看代码,一边对照文档,然后自己再动手做一做。当然这个例子要简单点才好,像GEF的那个logic的例子就太复杂了,即使是flow(运行界面见下图)我觉得也有点大;另外例子要比较规范的,否则学成错误的路子以后还要花时间改就不值得了。

file

图 用GEF编写的流程编辑器

GEF的结构决定了GEF应用程序的复杂性,即使最最简单的GEF程序也包含五六个包和十几个类,刚开始接触时有点晕是很正常的。我找到一个还不错的例子,当然它很简单了,如果你现在就想自己试试GEF,可以点这里下载一个zip包(若已无法下载请用这个链接),展开后是六个项目(pt1,pt2,…,pt6),每一个是在前面一个的基础上增加一些功能得到的,pt1是最简单的一个,这样你就可以看到那些典型的功能(例如DirectEdit、Palette等等)在GEF里应该怎样实现了。关于这个例子的更多信息请看作者blog上的说明

“Back in March, I talked a little about my initial attempts writing an Eclipse Graphical Editor Framework (GEF) application. I wanted, then, to write a tutorial that essentially walked the reader through the various stages of the development of my first application. I even suggested some kind of versioned literate programming approach to writing the tutorial and the code at the same time.

I haven't had time since then to make any progress, but I did get the GEF application to the stage where I had put together a snapshot at each of six milestones. A few people have written to me over the last six months asking the status of my tutorial and I've sent them my six snapshots as a starting point.

It makes sense for me to just to offer them here.

You can download a ZIP file with the six snapshots at http://jtauber.com/2004/gef/gef.zip.

Hopefully they are still useful, even without a surrounding tutorial.”

需要注意一点,这个例子应该是在Eclipse 2.1里写的,所以如果你想在Eclipse 3里运行这个例子,要修改plugin.xml里的dependencies为:

<import plugin="org.eclipse.core.resources"/> 
<import plugin="org.eclipse.gef"/>
<import plugin="org.eclipse.ui"/> 
<import plugin="org.eclipse.core.runtime"/> 
<import plugin="org.eclipse.core.runtime.compatibility"/> 
<import plugin="org.eclipse.ui.views"/> 

再修改一下DiagramCreationWizard这个类finish()方法里page.openEditor(newFile);这句改为page.openEditor(new FileEditorInput(newFile),"com.jtauber.river.editor");,还有一些warning不太影响,可以不用管。

或者如果你不是特别着急的话,留意我这个半新手写的GEF入门系列帖子,说不定能引起你更多的共鸣,也是一个办法吧。

GEF的学习周期是比较长的,学之前应该有这个心理准备。特别是如果你没有开发过Eclipse插件,那么最好先花时间熟悉一下Eclipse的插件体系结构,这方面的文章还是很多的,也不是很难,基本上会开发简单的Editor就可以了,因为GEF应用程序一般都是在Editor里进行图形编辑的。另外,绝大多数GEF应用程序都是基于Draw2D的,可以说GEF离不开Draw2D,而后者有些概念很难搞明白,加上其文档比GEF更少,所以我会从Draw2D开始说起,当然不能讲得很深入,因为我自己也是略知皮毛而已。

说实话,我对写这个系列不太有信心,因为自己也是刚入门而已。但要是等到几个月后再写,很多心得怕是讲不出来了。所以还是那句话,有什么写错的请指正,并且欢迎交流。

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2005/02/05/102513.html

给表格的单元格增加编辑功能(In place edit)

使用纯粹的SWT可以实现在单元格中编辑(In place edit)的功能,代码见这个例子,这里要说的是利用jface完成差不多的工作:用户单击单元格,出现一个下拉菜单,用户通过选择来改变单元格所代表的该行对象的属性。

对org.eclipse.jface.viewers.TableViewer.TableViewer的介绍这里就不赘述了,一般我们都是像下面这样定义TableViewer的:

TableViewer viewer = new TableViewer(...); 
viewer.getTable().setHeaderVisible(true); 
viewer.setContentProvider(...); 
viewer.setLabelProvider(...); 
//Add table columns 
TableColumn column = new TableColumn(viewer.getTable(), SWT.NONE); 
column.setText("..."); 

要增加编辑功能,首先得定义一个org.eclipse.jface.viewers.CellEditor数组,对应TableViewer的每一列,在我们这个例子里,第二列(Column 1)需要用下拉框编辑,所以可以这样定义这个CellEditor数组。

final CellEditor[] editors = new CellEditor[tvOre.getTable().getColumnCount()]; 
editors[1] = new ComboBoxCellEditor(viewer.getTable(), new String[] {}, SWT.READ_ONLY); 
viewer.setCellEditors(editors);
viewer.setColumnProperties(columnNames); 

再定义这个TableViewer的CellModifier(org.eclipse.jface.viewers.ICellModifier),该接口定义了三个方法,canModify()指出该单元格是否可被编辑;getValue()应返回单元格的当前值,因为我们使用的是下拉框,所以要返回一个Integer表示当前选中的index;modify()方法中我们将用户修改的值反映到实际模型中。代码如下,注意代码里IOre是我们模型的一部分,表格中每一行是一个IOre对象,IOre.getOrderParams()得到的就是下拉框中的选项,每行的选项与那一行代表的IOre对象有关:

viewer.setCellModifier(new ICellModifier() { 
    public boolean canModify(Object element, String property) { 
        IOre ore = (IOre) element; 
        String[] items = new String[ore.getOrderParams().size()]; 
        for (int i = 0; i < ore.getOrderParams().size(); i++) { 
            NameValuePair pair = (NameValuePair) ore.getOrderParams().get(i); 
            items[i] = pair.getValue(); 
        } 
        editors[1] = new ComboBoxCellEditor(viewer.getTable(), items, SWT.READ_ONLY); 
        return property.equals(columnNames[1]); 
    } 

    public Object getValue(Object element, String property) { 
        if (property.equals(columnNames[1])) { 
            IOre ore = (IOre) element; 
            for (int i = 0; i < ore.getOrderParams().size(); i++) { 
                NameValuePair pair = (NameValuePair) ore.getOrderParams().get(i); 
                if (pair.getValue().equals(ore.getOrderParamValue())) 
                    return new Integer(i); 
            } 
            return new Integer(0); 
        } 
        return null; 
    } 

    public void modify(Object element, String property, Object value) { 
        TableItem item = (TableItem) element; 
        IOre ore = (IOre) item.getData(); 
        NameValuePair pair = (NameValuePair) ore.getOrderParams().get(((Integer) value).intValue()); 
        ore.setOrderParamValue(pair.getValue()); 
        viewer.refresh(); 
    } 
}); 

接下来……哦,原来已经大功告成了,真是太容易了!不相信吗,请看下面的运行结果。

file

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2005/01/25/97336.html

SWT的GridData中一个需要注意的地方

如果在界面上有一个大文本框,一般我们会指定它的GridData为GridData.FILL_BOTH,这样在窗口改变大小时它的大小会随着变化。但在同一个GridLayout里,如果有多个这样的文本框,一定要注意它们的GridData中的grabExcessHorizontalSpace属性,该属性指出当一行中所有单元格的大小(宽/高)还不足以填充父控件时,是否增大其尺寸。

举一个具体的例子,有两行控件,父控件的Layout定义为new GridLayout(4,false),既有四列。第一行控件依次为Label0、Text1和Button2,其中Text3占2列;第二行为Text2,独占4列,我们希望第一行中的按钮右端和第二行中的文本框右端对齐,两个文本框都随窗口变宽而变宽,代码如下:

import org.eclipse.swt.*; 
import org.eclipse.swt.custom.*; 
import org.eclipse.swt.graphics.*; 
import org.eclipse.swt.layout.*; 
import org.eclipse.swt.widgets.*; 

public class MyLayout { 
    public static void main (String [] args) { 
        Display display = new Display (); 
        Shell shell = new Shell (display); 
        GridLayout gridLayout = new GridLayout (); 
        gridLayout.numColumns = 4; 
        shell.setLayout (gridLayout); 

        Label label0 = new Label (shell, SWT.NONE); 
        label0.setText ("label0"); 

        Text text1 = new Text (shell, SWT.BORDER); 
        text1.setText ("text1"); 
        GridData data = new GridData (); 
        data.horizontalAlignment = GridData.FILL; 
        data.horizontalSpan = 2; 
        data.grabExcessHorizontalSpace = true; 
        text1.setLayoutData (data); 

        Button button2 = new Button (shell, SWT.PUSH); 
        button2.setText ("button2"); 

        Text text3 = new Text (shell, SWT.BORDER); 
        text3.setText ("text3"); 
        data = new GridData (); 
        data.horizontalAlignment = GridData.FILL; 
        data.verticalAlignment = GridData.FILL; 
        data.horizontalSpan = 4; 
        data.grabExcessHorizontalSpace = true; //Wrong! 
        text3.setLayoutData (data); 

        shell.pack (); 
        shell.open (); 

        while (!shell.isDisposed ()) { 
            if (!display.readAndDispatch ()) 
                display.sleep (); 
        } 
        display.dispose (); 
    } 
}

因为想到两个文本框的宽度要变大,所以指定它们的GridData的grabExcessHorizontalSpace属性为true,而运行结果却与我们想的有差别。原因是下面的文本框实际上在宽度上占据了第一行中最靠两边的两个单元格,使得第一行中间的两个单元格宽度变小。为了解决这个问题,只要把定义第二个文本框Text3的grabExcessHorizontalSpace的语句去掉即可(缺省值为false)。

还需要注意,有时候为了简单起见,我们常使用GridData data=new GridData(GridData.FILL_BOTH)来定义GridData,这个FILL_BOTH上包含了FILL_HORIZONTAL和FILL_VERTICAL,而它们各自又包含了HORIZONTAL_ALIGN_FILL、GRAB_HORIZONTAL以及VERTICAL_ALIGN_FILL、GRAB_VERTICAL。所以,如果你是用构造方法定义的GridData,并且又希望不要grabExcessHorizontalSpace,则应该手动指定它为false才行。

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2005/01/17/93269.html

给Eclipse插件的View加上菜单和工具条

Eclipse的每个视图(View)都有自己的菜单和工具条,View通过与自己相关的IViewSite对象与这些东西打交道,确切的说,是通过这个IViewSite对象的IActionBars对象来管理,ActionBars对象负责菜单、工具条和状态栏。

file

一个典型的View(继承org.eclipse.ui.part.ViewPart)的代码结构会是这样,作为例子,假设我们有三个功能项:Open、Remove和Reload,我们的View是一个简单的表格TableViewer,里面显示一些条目列表,允许用户进行多选:

TableViewer tvResult;
OpenAction openAction;
RemoveAction removeAction;
ReloadAction reloadAction;
public void createPartControl(Composite parent) {
    //创建视图界面

    //创建菜单
    createActions();
    createMenu();
    createContextMenu();
    createToolbar();
    hookGlobalActions();
}

其中,createActions()是创建必要的IAction对象,这些对象可用在菜单、工具条里;createMenu()的作用是把刚刚创建的IAction对象放进与View相关的MenuManager里,就像前面所说,MenuManager可以通过getViewSite().getActionBars().getMenuManager()方法得到;createToolbar()则是把同样的对象放在工具条里,获得工具条的方法与菜单类似;createContextMenu()则是建立鼠标右键触发的上下文菜单,方法是建立一个新的MenuManager,然后由它建立一个Menu对象,再将Menu对象与控件联系;hookGlobalActions()的作用是把IAction对象与系统菜单(而不是View菜单联系),达到同一菜单项对不同View具有不同响应的效果。下面来看一下具体代码:

package net.sf.solo.actions;

import java.io.IOException;
import java.util.Iterator;
import net.sf.solo.model.IInstance;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;

public class OpenAction extends Action implements ISelectionChangedListener {

    IStructuredSelection selection;

    public OpenAction() {
        setEnabled(false);
    }

    public void run() {
        for (Iterator iter = selection.iterator(); iter.hasNext();) {
            IInstance ins = (IInstance) iter.next();
            try {
                //TODO Only in windows can do this.
                Runtime.getRuntime().exec("cmd /E:ON /c start " + ins.getReferenceURL());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public void selectionChanged(SelectionChangedEvent event) {
        selection = (IStructuredSelection) event.getSelection();
        setEnabled(selection.size() > 0);
    }

    public String getText() {
        return "&Open in browser";
    }
}

上面是一个例子IAction,它的作用是在触发时将用户选中的条目在浏览器里打开。这个类同时还实现了ISelectionChangeAction,这样就可以在用户没有选中任何条目的时候将自己变为不可用。当然,你要把它作为监听器加入某个列表对象的监听器列表,像下面代码里这样:

private void createActions() {
    openAction = new OpenAction();
    removeAction = new RemoveAction(tvResult);
    reloadAction = new ReloadAction(tvResult);
    tvResult.addSelectionChangedListener(openAction);
    tvResult.addSelectionChangedListener(removeAction);
    tvResult.addSelectionChangedListener(reloadAction);
}

注意,最后的三句就是加入监听列表的功能。有些IAction需要改变所监听对象(比如一个TableViewer)的行为,所以要把那个对象作为参数传递给它才行。下面是把IAction对象加入菜单的代码:

private void createMenu() {
    IMenuManager mgr = getViewSite().getActionBars().getMenuManager();
    mgr.add(openAction);
    mgr.add(removeAction);
    mgr.add(reloadAction);
}

把IAction对象加到工具条的代码几乎完全一样,只是第一句有所不同:

private void createMenu() {
    IToolBarManager mgr = getViewSite().getActionBars().getToolBarManager();
    mgr.add(openAction);
    mgr.add(removeAction);
    mgr.add(reloadAction);
}

上下文菜单就显得有些麻烦了,因为View并没有一个“PopupMenuManager”这样的东西,所以我们只能半手动的来建立:

private void createContextMenu() {
    MenuManager mgr = new MenuManager();
    mgr.setRemoveAllWhenShown(true);
    mgr.addMenuListener(new IMenuListener() {
        public void menuAboutToShow(IMenuManager manager) {
            fillContextMenu(manager);
        }
    });
    Menu menu = mgr.createContextMenu(tvResult.getControl());
    tvResult.getControl().setMenu(menu);
    getSite().registerContextMenu(mgr, tvResult);
}

这个MenuManager和我们在createMenu()里通过getMenuManager()得到的是同一个类(但不是同一个实例哦),setRemoveAllWhenShown(true)的作用是清空以前显示的菜单项,当触发了menu事件时,重新填充(fillContextMenu),所以如果你不把removeAllWhenShow置为true的话,每点一下右键你就会看到菜单项多出一倍来。Menu是swt的控件(刚才说的MenuManager、ToolbarManager都是jface里的东西,jface给swt包了一层),用MenuManager可以创建出一个Menu对象,然后我们用表格的setMenu方法将表格控件与Menu控件联系在一起就好了。

最后还有一句,它是为扩展这个上下文菜单用的,例如你可以在plugin.xml里统一指定给某种类型的元素都加上某个菜单项(例如,如果用户选中了一个.zip文件,会多出一个“解压缩”选项)。那么新加的菜单项会出现在上下文菜单的哪里呢,最上方还是最下方,还是……?所以呢,要在fillContextMenu的时候指定一下:

protected void fillContextMenu(IMenuManager manager) {
    manager.add(openAction);
    manager.add(removeAction);
    manager.add(reloadAction);
    manager.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS));

}

前三句都没什么特别,最后一句就是指定了上面我们说的这个“增加点”,这样,你想让后来的菜单放在哪里都行了。

最后,Eclipse的Workbench提供了一些比较通用的系统菜单项,如下:

public static final String [] GLOBAL_ACTIONS = {
        UNDO,
        REDO,
        CUT,
        COPY,
        PASTE,
        PRINT,
     DELETE,
        FIND,
     SELECT_ALL,
        BOOKMARK
};

当你的焦点在不同的View或Editor里时,同一个系统菜单项会有不同的作用产生。例如在文本编辑器中delete项是删除当前选中的文字,而在你的视图里,你希望delete的作用是删除用户选中的表格条目,刚好是removeAction的功能。所以你要把你的IAction对象和系统菜单挂在一起:

private void hookGlobalActions() {
      IActionBars bars = getViewSite().getActionBars();
      bars.setGlobalActionHandler(IWorkbenchActionConstants.DELETE, removeAction);
}

注意,要选择语义上比较相近的系统菜单项来挂接,否则会造成用户的困扰。比如你非要把COPY实现为openAction,当用户在系统菜单里选了copy命令,本以为会把当前选中的条目复制到剪贴板,你却给人家打开了这些条目,多滑稽。

好了,菜单方面基本上就这些内容。可以看出,Eclipse的Workbench的确为我们提供了很多方便,特别是如果能够灵活利用plugin来进行定义,不仅可以节约大量的代码,还能让我们始终保持对系统的掌握。所以说,RCP的风行可不是没有道理哦。

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2005/01/14/92122.html

[Eclipse]国际化你的应用程序(下)

在本文的上篇里,介绍了使用Eclipse的国际化工具对程序中的字符串进行外向化处理(Extenalize),可以看出步骤是十分简单的。实在是很喜欢Eclipse这样的工具,它可以为你做很多事情,干净漂亮,但绝不会在未经你同意的情况下做任何动作,所谓“利器”也!

现在说说在资源中含有参数的情况怎样处理。比如在对话框中要显示信息:“帐户目前还有 900 元,截止日期为 2004-9-1,谢谢!”,因为中间的数字和日期是动态的,所以不能直接放在资源文件中。但是请放心,大可不必为这条信息指定三个资源(被数字和日期分开的三个字符串),可以在资源文件(.properties)中指定资源为这个样子:

my.resource.key=账户目前还有 {0} 元,截止日期为 {1},谢谢!

其中{0}和{1}表示将替换为动态的值,然后在程序里写:

Float amount = ...;
Date dt = ...;
String msg=MessageFormat.format(Messages.getString("my.resource.key"), new Object[]{amount,dt});

这样,msg变量里就是动态生成的提示信息了。你很可能希望对日期进行格式化处理,要实现这个功能也很简单,只要稍微修改一下资源,如下:

账户目前还有 {0} 元,截止日期为 {1,date,yyyy-MM-dd},谢谢!

确实简单吧,不知道你用没用过这个写法,我是在PPP项目中才第一次使用的,所以赶紧介绍一下了,呵呵。

接下来的问题是编码,资源文件写为英文是没有问题的,可以正常显示,但汉字是不能直接写在资源文件里的,要转换为unicode才可以。jdk本身提供了native2ascii工具,可以实现这个功能,但用命令行总是不太方便,虽然也有人很喜欢使用命令行的感觉……如果你愿意Eclipse为你服务,大可以使用我下面介绍的两个插件,利用它们,你根本不需要显式转换编码这一步了。

第一个是Properties Editor,好象是日本人写的,安装后它会与扩展名为.properties的文件相关联,使用它打开资源文件,可以在本地语言与unicode视图之间切换,一般情况下编辑本地语言就可以了,保存时会自动转换为unicode。当需要查找某个资源(值)并修改时这个编辑器非常方便,例如想把资源里所有“你好”改为“您好”,如果面对的是一堆unicode码还真是头疼。安装这个插件需要2.1.1版本以上的Eclipse。

另一个我们在项目中经常使用的插件叫……我还真不知道它全名叫什么,在我这里的打包文件名是“29 Localization Editor”,它是一个非常方便的国际化翻译工具。用它打开扩展名为.properties的文件后,可以新建key,或者新建语言,你要做的只是在表格中把尚未翻译成新语言的资源值填下而已,可以选择只显示未翻译的条目或是全部条目。不过很对不起,我还没找到网上的下载地址,如果需要请和我联系吧。

file
图1 Localization Editor使用界面

好了,关于Eclipse的国际化先介绍到这里了。如果你的应用程序是Eclipse插件,还可以更进一步:把资源文件打成语言包。关于这种方式(Fragment)的介绍,以后有时间再写吧。

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2004/08/23/36011.html

[Eclipse]国际化你的应用程序(上)

file

记得几年前汉化软件一般是用二进制编辑工具在编译后的文件中查找和替换英文单词,这个过程需要使用很多技巧,非常的麻烦。而现在,在开发时对应用程序进行国际化处理已经越来越成为一个必不可少的步骤了。例如这次我参与的项目是给台湾客户做的,他们要求英文和繁体中文两个版本,幸好我们使用的开发工具是Eclipse,利用它的国际化功能可以很方便的将写在代码里的字符串提出到独立的资源文件中,这里用一个简单的例子说明一下这个过程。

在Eclipse里建立一个名为nls-test的工程(也可以国际化已有工程),新建一个类NLSTest,内容如下:

public class NLSTest {
   public NLSTest() {
      String str1="Hello world!";
      System.out.println(str1);
   }
   public static void main(String[] args) {
       new NLSTest();
   }
}

现在,这个类的输出是在代码里写死的,如果要改变就必须修改代码然后重新编译。下面我们利用Eclipse解决这个问题。在导航器(Package Explorer)里右键单击这个文件,选择Source -> Externalize Strings,就会打开一个对话框,在这里列出了该类中尚未国际化的字符串,见下图。

file
图1 国际化向导第一步

单击每个字符串前面的小方块可以选择对该串 1、进行国际化处理 2、永不进行国际化处理 3、这次不处理。我们选择要对这个字符串进行国际化处理,并把它的Key修改为比较好理解的名称hello,注意在对话框最上方可以指定一个通用的前缀,这个值会加在每个Key前面作为最终写在资源文件(扩展名是.properties)里的Key名称。好,按下一步继续。

在这里要指定properties文件的名称,如果需要的话要指定一个用于从properties文件中取资源的类,一般是XXXMessages的格式,再按下一步,会显示一个所作更改的确认列表,确认后按Finish按钮完成向导。

可以看到Eclipse为我们生成了NLSTestMessages.java和NLSTest.properties,打开后者会看到里面只有这么一句:

NLSTest.hello=Hello world!

而前者NLSTestMessages的作用是根据参数Key从后者取对应的字符串值,看一看现在的NLSTest.java就知道了,它的内容现在是这样的(只列出构造方法,main方法同上):

public NLSTest() {
    String str1=NLSTestMessages.getString("NLSTest.hello"); //$NON-NLS-1$
    System.out.println(str1);
}

后面的注释是Eclipse自己用的,标有这个注释的字符串在国际化将被忽略。注释中的数字1表示要忽略的是该行中第一个字符串,如果一行语句里有多个字符串被忽略,将会有多个这样的注释,但数字会各不相同,像这样:

//$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 

好了,现在这个类的国际化处理就算完成了。要想让这个类可以根据用户所在地区输出不同语言的结果,可以在NLSTest.properties同一目录下创建名为NLSTest_XX.properties的文件,其中XX表示国家名称,例如中国是zh_CN或zh_TW,法国是FR等等。新创建的文件里也要有和原来文件相同的名值对,但值是不同语言的,NLSTestMessages类会根据用户机器的地区设置值自动从不同的资源文件里取值,这样就达到了国际化的目的。要在自己的机器上测试运行结果,可以在Eclipse的运行设置里面加上这样的参数:-nl zh_TW,这样就不用费力气设置区域了。(2012/5/4更新:此方法可能有误,在Eclipse3.6里是启动程序时在VM arguments里加上-Duser.language=XXX即可以所需要的语言环境启动程序)

国际化的原理很简单,Eclipse提供的这个功能使国际化变得更容易了。不过关于国际化还有一些细节问题,包括对含参数资源的处理,字符编码处理等等,下篇将对它们进行讨论。

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2004/08/08/31262.html