Eclipse零起步(转)(7~9)

来源:互联网 发布:商家怎样找淘宝客 编辑:程序博客网 时间:2024/06/02 13:09

Eclipse零起步(七)

(前言)
上篇我们讲了lomboz和jboss/tomcat的配置,和lomboz的web开发, 今天我们讲一个用lom
boz开发ejb的例子.

(正文)
[使用 lomboz 开发一个 stateless session bean]

现在我们试着用lomboz开发一个简单的ejb, 这个bean主要负责用户身份认证.数据库操
作使用DAO技术.
主要的需求有: 所有的用户,供应商和经理都有唯一的一个username和userid来登录,如
果要登录,则需要使用方法login(),这个方法有两个函数,一个是username,一

个是password, 如果身份认证通过, 则返回userid

接下来让我们看一下完成这个ejb要做的工作:

1. 创建一个j2ee项目叫MyStore
2. 创建一个stateless session bean 叫 StoreAccess
3. 添加一个业务方法,叫public String loginUser (String username, String
password)
4. 创建一个DAO名叫StoreAccessDAOImpl,生成DAO的接口
5. 在DAO中,实现loginUser方法
6. 添加回调函数,并实现
7. 部署ejb
8. 创建测试客户端
9. 运行客户端,测试ejb

----------------------------

1. 创建j2ee项目

点击菜单 file->new->project, 在new project 对话框里选择java->lomboz j2ee
wizards->lomboz j2ee project,按next,

填入项目名MyStore, 按next,

在java setting对话框,翻到source页面, 设置项目的源目录为MyStore/src, 输出目录
为MyStore/bin, 按next

在create j2ee module对话框, 翻到web module页面, 然后添加一个module名字叫Onli
neStore

再翻到ejb module页面, 然后添加一个module名字叫:MyStoreMgr

再翻到targeted server页面下,添加jboss 3.2.x服务器(就是我们昨天陪的jboss4.0.0,
因为是ejb,所以tomcat5.0.x不能用)


2.创建stateless session bean

创建完项目后, 点击菜单 file->new->other, 在new对话框中选择java->lomboz j2ee
wizards->lomboz ejb creation wizard,按next

在create ejb对话框中设定以下值:
source folder: MyStore/src
package: edu.sjtu.eclipse.session
Name: StoreAccess
ejb type: Stateless Session Bean
然后按finish



以下是这个bean生成的代码

------------------------------

package edu.sjtu.eclipse.session; import javax.ejb.SessionBean; /** * @ejb.bean name="StoreAccess" *  jndi-name="StoreAccessBean" *  type="Stateless"  *  *-- * This is needed for JOnAS. * If you are not using JOnAS you can safely remove the tags below. * @jonas.bean ejb-name="StoreAccess" *  jndi-name="StoreAccessBean" *  *-- **/ public abstract class StoreAccessBean implements SessionBean { }

----------------------------------

其中注释里标明了这个ejb的name,type,和jndi-name, 这个到时候要生成部署描述符派
用场,写到ejb-jar.xml 和 jboss.xml中去的

如果你展开此项目的MyStoreMgr/META-INF目录,你会发现有很多***.xml,这些都是描述,
部署ejb要用的

接下来,我们来创建包括home,remote,DAO,helper的接口类

打开/MyStoreMgr/META-INF/xdoclet.xml (也有的是叫ejbCreate.xml, 名字其实无所谓
的,只要内容符合XDoclet规范就可以)

这个文件在你生成ejb时就自动生成了, 它能帮助你生成接口和helper类


这个node,配置了DAO信息,不用修改了

version="3.0"
unauthenticatedPrincipal="nobody"
xmlencoding="UTF-8"
destdir="${ejb.dd.dir}"
validatexml="false"
datasource="PLEASE_MODIFY_THIS"
datasourcemapping="PLEASE_MODIFY_THIS"
preferredrelationmapping="PLEASE_MODIFY_THIS"
/>

这是jboss的配置信息,我们要做一下修改,改成:

version="3.0"
unauthenticatedPrincipal="nobody"
xmlencoding="UTF-8"
destdir="${ejb.dd.dir}"
validatexml="false"
datasource="java:/DefaultDS" //这个是数据源的jndi-name
datasourcemapping="Hypersonic SQL" //映射到一个xml节点,这个
节点定义了数据结构
preferredrelationmapping="foreign-key" //数据库类型
/>


3.创建DAO接口

我们创建一个class作为DAO的实现, file -> new -> class, class的package为edu.sjt
u.eclipse.dao, name为 edu.sjtu.eclipse,以下是自动生成的代码:

---------------------------------
package edu.sjtu.eclipse.dao; public class StoreAccessDAOImpl { }

---------------------------------

然后让一开始设计的StoreAccessBean,改一下代码:

--------------------------------
package edu.sjtu.eclipse.session; import javax.ejb.SessionBean; /** * @ejb.bean name="StoreAccess" *  jndi-name="StoreAccessBean" *  type="Stateless"  *  *-- * This is needed for JOnAS. * If you are not using JOnAS you can safely remove the tags below. * @jonas.bean ejb-name="StoreAccess" *  jndi-name="StoreAccessBean" *  *-- * @ejb.dao class="edu.sjtu.eclipse.session.StoreAccessDAO"    //添加对dao的访问描述 * impl-class="edu.sjtu.eclipse.dao.StoreAccessDAOImpl" **/ public abstract class StoreAccessBean implements SessionBean { }

--------------------------------

然后点击StoreAccessBean文件,然后右键菜单 Lomboz J2ee->add ejb to module, 这样
就可以把我们设计的bean添加到MyStoreMgr 这个module里去了

然后点击MyStoreMgr目录,右键菜单 Lomboz j2ee-> generate ejb classes, 此时就会
自动生成我们所需要的classes,并放置在ejbsrc这个目录

+ejbsrc
+ edu.sjtu.eclipse.session
-StoreAccess.java
-StoreAccessDAO.java
-StoreAccessHome.java
-StoreAccessLocal.java
-StoreAccessLocalHome.java
-StoreAccessSession.java
-StoreAccessUtil.java

4.创建business方法

右键点击StoreAccessBean.java,菜单 lomboz j2ee -> add ejb methods, 按next
添加一个remote的business方法: public String loginUser(String username,
String password)

则在这个bean里面,多了一个方法
----------------------------------------
    /**     * @ejb.interface-method     *  view-type="remote"      * @dao.call name="loginUser"         <- // 添加这句话,用来调用DAO    **/    public String loginUser(String username, String password) {        return null;    }

----------------------------------------

再次Generate EJB Classes, 然后看看生成的classes和原来的有什么不同

5.实现DAOImpl方法

----------------------------------------
package edu.sjtu.eclipse.dao; import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException; import javax.naming.InitialContext;import javax.naming.NamingException;import javax.sql.DataSource; import edu.sjtu.eclipse.session.StoreAccessDAO; public class StoreAccessDAOImpl implements StoreAccessDAO {     private DataSource jdbcFactory;     public void init() {         System.out.println("Entering StoreAccessDAOImpl.init()");        InitialContext c = nullif (null == this.jdbcFactory) {            try {                c = new InitialContext();                this.jdbcFactory =                    (DataSource) c.lookup("java:comp/env/jdbc/DefaultDS");            } catch (NamingException e) {                System.out.println("Error in StoreAccessDAOImpl.init()");            }        }         System.out.println("Leaving StoreAccessDAOImpl.init()");     }     public String loginUser(String username, String password) {         System.out.println("Entering StoreAccessDAOImpl.loginUser");        Connection conn = null;        PreparedStatement ps = null;        ResultSet rs = null;        String userID = null;        try {            conn = jdbcFactory.getConnection();            String queryString =                "select userid from storeaccess where username = ? and password = ?";            ps = conn.prepareStatement(queryString);            ps.setString(1, username);            ps.setString(2, password);            rs = ps.executeQuery();            boolean result = rs.next();            if (result) {                userID = rs.getString("userid");                System.out.println("Userid is " + userID);             }        } catch (SQLException e) {            e.printStackTrace();            System.out.println("Inside StoreAccessDAOImpl.loginUser" + e);        } finally {            try {                rs.close();                ps.close();                conn.close();            } catch (Exception e) {             }        }        System.out.println("Leaving StoreAccessDAOImpl.loginUser");        return userID;    } }

----------------------------------------

6.在bean中设置context

----------------------------------------------------------------
public abstract class StoreAccessBean implements SessionBean {     /**     * @ejb.interface-method     *  view-type="remote"      * @dao.call name="loginUser"    **/    public String loginUser(String username, String password) {        System.out.println("Entering StoreAccessBean");        System.out.println("Leaving StoreAccessBean");        return null;    }        //添加这个sessioncontext,并实现下面两个函数    protected SessionContext ctx;     public void setSessionContext(SessionContext ctx){        this.ctx  = ctx;    }        public void unsetSessionContext(){        this.ctx = null;    }}

-------------------------------------------------------------------

然后再generate ejb classes,生效

其实你已经发觉了, 生成出来的ejb类,如StoreAccessSession是继承了StoreAccessBean
这个类, 而且对于bean实现的函数,会自动用super来替代.


7. 部署bean

添加部署的一些配置

/**
* @ejb.bean name="StoreAccess"
* jndi-name="StoreAccessBean"
* type="Stateless"
*
*--
* This is needed for JOnAS.
* If you are not using JOnAS you can safely remove the tags below.
* @jonas.bean ejb-name="StoreAccess"
* jndi-name="StoreAccessBean"
*
*--
* @ejb.dao class="edu.sjtu.eclipse.session.StoreAccessDAO"
* impl-class="edu.sjtu.eclipse.dao.StoreAccessDAOImpl"
*
* @ejb.resource-ref res-ref-name="jdbc/DefaultDS" <-- 从这行开始添

* res-type="javax.sql.Datasource" <-- 这样它会自动
把这些配置
* res-auth="Container" <-- 加到ejb-jar.x
ml
* <-- 和jboss.xml中

* @jboss.resource-ref res-ref-name="jdbc/DefaultDS"
* jndi-name="java:/DefaultDS"
*
**/

然后再次generating ejb classes,然后就可以测试咯~~~, 呼..., 为了做个这东西还真
不容易

8. 测试

右键点击MyStoreMgr,然后菜单lomboz j2ee->run server,启动服务器

启动完毕后, 再右键点击MyStoreMgr, lomboz j2ee-> deploy module, 这样我们做的My
StoreMgr就部署上去了

然后新建一个lomboz j2ee test client, 用new向导

在创建这个client向导的对话框中,填入:

source folder: MyStore/src
package: edu.sjtu.eclipse.client
Name: SessionClient
Ejb home: edu.sjtu.eclipse.session.StoreAccessHome
ejb interface: edu.sjtu.eclipse.session.StoreAccess
target server: jboss 3.2.x(jboss 4.0.0)

打开SessionClient.java, 在测试函数testBean(),加入
--------------
System.out.println("Request from client : ");System.out.println("Reply from Server: Your userid is "        + myBean.loginUser("ECLIPSE", "PASSWD"));


--------------

然后运行这个application,从console中看到:
Request from client :
Reply from Server: Your userid is null

呵呵, 数据里面没有东西的,所以是null, 如果想加的话,看看jboss的说明
我们用的是jboss里自带的那个Hypersonic SQL

Eclipse零起步(八)

(前言)
哈~, 今天我们开始一起学习做插件吧, 其实我觉得到今天我们才开始进入正题也. 就像
光会吃鱼不行,我们还要学会钓鱼啊.

(正文)

[Hello World 例子]

先新建一个项目, Plug-in Development -> Plug-in Project, 按next
项目名就叫MyPlugin, 按next
plugin的项目结构不用改,按next
在plugin code generator选择 Create a plug-in project using code generation
wizard -> hello world, 然后按next,
之后的页面,我们都用默认的,后面我们会讨论如何改的,按finish

现在, hello world这个例子就已经生成好了, 估计你会发现tasks视图会有错误, 说xx
plugin不能解析之类的话, 所以我要做一些工作台的设置

打开preference -> plugin environment -> target platform, 然后按 not in
workspace按钮, 这个是pde插件内置的一个设置, 因为我们是做插件, 编译时肯定

要用到很多eclipse 原有插件的运行库, 做了找个设置之后, pde在编译项目时就会根据
这个设置,计算构建路径,编译我们的插件.

在eclipse 2.0版本时,做的还没有这么先进, 开发要依赖的插件必须导入到工作区,作为
java 项目来应用才可以进行编译, 到2.1版本后,这个问题就解决了, 开发

插件也更方便了.

[运行 Hello World]

接下来让我们快来运行一下我们的插件来测试一下,
点击菜单的run, 然后我们new一个workbench的运行配置,


我们运行插件可不是普普通通的java application 就能搞定的哦,因为插件和eclipse平
台是一根绳上的蚂蚱,谁也少不了谁,所以我们只能在一个新建的工作台上运

行我们的插件.
然后就直接run吧
这时你会发现又有一个eclipse跑起来了, 就是我们测试用的平台

等新的工作台启动后, 我们来看看我们插件运行的效果, 看菜单里多了一个sample
menu
我们点击sample menu -> sample action, 会弹出一个对话框, 写着 Hello, Eclipse
World!

这就说明我们的插件已经成功运行了, 呵呵, 鼓掌~~
, 怎么样啊, 有成就感吧, 比那个xx lomobz用起来简单多了,哈哈


[分析 Hello World]


那接下来我们要好好分析一下前面神奇的过程了.
其实,学习插件一开始,我们就要学会怎么编写plugin.xml,因为一切的神奇都是从这里开

我们双击plugin.xml这个文件,然后会出现一个多页编辑器,这个编辑器是pde提供用来开
发plugin而设计的
我们先跳过这个编辑器的功能,直接这个编辑器的source页,我们就可以plugin.xml的原
貌了,我们来一点一点介绍这个东东:

---------------------------------------------------
// xml 的头, 不用care的

//接下来,我们要定义一个插件, 定义一个插件是有几个属性值
//id 是plugin的唯一身份标识
//name 是plugin的名字
//version 是这个插件的版本, 1.0.0 主版本.次版本.小版本
//一般来说, 插件的功能有革命性变化, 可以升主版本
//插件的功能上有些增加,就可以升次版本
//对于插件的bug的fix后,就可以升小版本
//provider-name 表明是谁做的,你可以把你的大名写上去,呵呵
//class 是这个插件的主类, 都是一些helper方法
id="MyPlugin"
name="MyPlugin Plug-in"
version="1.0.0"
provider-name=""
class="MyPlugin.MyPluginPlugin">

//这个是运行时的包, 我们src目录下编写的那些东东,在发布成插件时,要打成这个jar
//这样插件运行的时候,就可以找到这些类




//插件是互相依赖的,如果你要用到其他插件,
//那你要在requires里面声明, 这样等于是把这个插件的classpath给设好了
//要不然运行的时候,会找不要其他插件的这些运行库的






//这个是extension是eclipse最有特色的一个概念
//你可以把这个extension认为是eclipse提供的插座
//有了它我们就可以无限制的扩展我们的eclipse
//比如这里org.eclipse.ui这个插件提供了一个extension
//这个extension叫做org.eclipse.ui.actionSets
point="org.eclipse.ui.actionSets">

label="Sample Action Set"
visible="true"
id="MyPlugin.actionSet">

//我们先定义了一个menu, 叫做Sample Menu,就是我们运行的时候看到的
label="Sample &Menu"
id="sampleMenu">
name="sampleGroup">



//在定义一个action,叫做Sample Action,
//menubarpath定义了这个action放在菜单哪个位置
//toolbar定义了这个action放在工具栏的哪个位置
//class是这个action的实现

label="&Sample Action"
icon="icons/sample.gif"
class="MyPlugin.actions.SampleAction"
tooltip="Hello, Eclipse world"
menubarPath="sampleMenu/sampleGroup"
toolbarPath="sampleGroup"
id="MyPlugin.actions.SampleAction">




//这里又是一个extension
//作用就是让resource透视图默认打开时,
//前面定义的这个actionset可视的
//否则你还有自己customize perspective才能把这个menu显示出来
point="org.eclipse.ui.perspectiveExtensions">
targetID="org.eclipse.ui.resourcePerspective">
id="MyPlugin.actionSet">





----------------------------------

接下来,我们再看一下,这个action是怎么实现的

----------------------------------

//这个action类必须实现IWorkbenchWindowActionDelegate接口public class SampleAction implements IWorkbenchWindowActionDelegate {    private IWorkbenchWindow window;     public SampleAction() {    }     //这里就是action执行的实现函数    public void run(IAction action) {        //当action执行, 就打开一个messagedialog                //dialog上显示hello,eclipse world!                //这个dialog是封装在jface插件下的                //照理说这个插件就应该需要jface插件的支持                //不过这个插件依赖的是org.eclipse.ui这个插件                //而ui找个插件有依赖jface,swt等插件                //所以是嵌套依赖的        MessageDialog.openInformation(            window.getShell(),            "MyPlugin Plug-in",            "Hello, Eclipse world");    }     ......}



[extension 模式]

在分析hello world中, extension这个东西,也是eclipse里面设计的神奇之处

而提供extension的也是插件, 定义这些extension的结构, 我们叫它们为extension
point

你可以把extension point想为接口, 而我们扩展这些extension,其实就是实现了这个接


让我们来画个图,解释一下:


---------------------- --------------------
|plugin A | |plugin B |
| --------------- | contibute | -------------- |
| | ext point p | <--------------- | extension | |
| --------------- |_ | -------------- |
| || | | | || |
| --------------- | |implement | -------------- |
| | interface I | <--|------------ | class C | |
| --------------- | | | -------------- |
---------------------- | ----------// -------
| create, call |
----------------------|



假设plugin A定义了一个extension point, 在hello world 中就像org.eclipse.ui定义
了一个actionsets的extension point

假设plugin B定义了一个extension,是基于plugin A的这个extension point的,它的实
现类是class C,就想 hello world中的那个action

那eclipse启动后, 会读取每个plugin的配置plugin.xml,然后发现plugin B有一个基于e
xt point p的扩展

那它就会用interface I作为对象, 然后实例化一个class C, 就等于实现了这个extensi
on


让我们来看一段eclipse的内部实现

IPluginRegistry registry = Platform.getPluginRegistry();IExtensionPoint extensionPoint = registry.getExtensionPoint(xpid);IExtension[] extensions = extensionPoint.getExtensions();// For each extension ...for (int i = 0; i < extensions.length; i++) {    IExtension extension = extensions[i];    IConfigurationElement[] elements = extension.getConfigurationElements();    ......}



我们可以发现,eclipse就是注册每一个extension和extension point
然后用extension point来实例化它对应的那个extension


我想今天我们就通过一个简单的例子,让大家了解一下plugin的内部构造,以及介绍了一
个重要概念就是extension
当然以后自己做插件也可以自己做extension point,我想以后我们会谈到, 今天就这样吧

Eclipse零起步(九)

(前言)
昨天我们已经开发了一个简单的hello world插件了, 但是我们发现是英文界面的. 而用
过WSAD的朋友都应该知道, WSAD

是可以支持很多国家的语言的. 而且IBM也捐献给eclipse一个translation package, 装
了之后就可以有中文界面的

eclipse出来了. 那这个东东,eclipse到底是如何实现的呢? 我们来介绍一下.

(正文)

[Eclipse 2.1.x Translation Package]

你可以从eclipse.org 或者 交大的ftp 里 下载到 这个翻译包, 我们把它下载下来,安
装到eclipse中

然后我们在控制面板的区域设置中,设置自己所在的区域,比如中国(中华人民共和国)

然后,我们启动一下eclipse,你会发现界面都是中文显示了

如果设置为法国, 那就启动后就都是法文显示了

这个包之所以能够如此神奇, 都是靠了, resource bundle来解决的

其实eclipse还未开源前, wsad的国际化问题(internationalization = i18n), 一开始
作为IBM内部项目NLS, 进行研究

的. 这个项目的进行, 也逐渐是eclipse的i18n问题的解决方案成形了

[eclipse i18n 解决方案]

我们首先要找到所有表现层的地方,主要有这几块:

1. 文本: plugin.xml , 联机帮助文档 , GUI中的消息,标签
2. 数据格式
3. 地区和称谓


那我们一个个来讲:

比如标签,消息:
昨天那个hello world的例子中,当点击sample action就会显示hello ,eclipse world!
但是这只是在英文的界面显示, 那别的语言表示怎么办呢?

有办法, 用resource bundle, 怎么个用法, 我们后面会介绍.

然后就是plugin.xml了:

name="Java Development Tools UI"
id="org.eclipse.jdt.ui"
version="2.1.2"
provider-name="Object Technology International, Inc."
class="org.eclipse.jdt.internal.ui.JavaPlugin">

比如这是jdt的plugin.xml的一个描述, 这是没有i18n的plugin.xml

加入i18n解决方案的plugin.xml是这样的

name="%plugin.name"
id="org.eclipse.jdt.ui"
version="2.0.0"
provider-name="Object Technology International, Inc."
class="org.eclipse.jdt.internal.ui.JavaPlugin">

然后我们还有在plugin根目录下的文件plugin.properties

包含了以下信息:
plugin.name: Java Development Tools UI

我们把要翻译的字符串都拉到properties文件中, 对于不同的语言,只要翻译这些拉出来
的字符串,并存放到plugin_xx.

properties文件中就行了

联机帮助:
eclipse的帮助系统也可以进行翻译

数据格式: 包括数字,日期,时间,货币,不同语言国家的这些数据格式都是不同的
地区称谓: 每个国家都有自己的尊称,度量等等

[eclipse i18n 实现步骤]

1. 把要翻译的字符串装到 .properties文件中去

点菜单里source-> externalize string, 这个方法是外部化字符串, 这个功能会搜寻你
文件中所有常量字符串, 然后在

弹出对话框里,可以设定这些字符串的ID, 之后还有生成resource bundle类和相应的.pr
operties文件名设定的选项, 设

定好之后, 要翻译的字符串就给包进resource bundle中, 数据在xxx.properties文件中
, 那倒是只要相应的生成

xxx_de.properties, xxx_zh.properties等文件, 这样程序会根据不同的语言去读取不
同的properties, 这样显示的消

息也就是多国语言版了.

当然,如果你觉得那个字符串不需要国际化, 那就在这行的后面加一个注释 /*
NON-I18N */

举个例子:

  public class HelloWorld {     static public void main(String[] args) {         String a = "yes";         System.out.println("Hello, eclipse world!");     }   }


我们外部化后,如果用HelloWorldMessages类封装resource bundle , 数据文件为
HelloWorld.properties

那就会变成这样:

  public class HelloWorld {     static public void main(String[] args) {          String a = "yes"; //$NON-NLS-1$ <-- 这个说明字符串不需要翻译          System.out.println(HelloWorldMessages.getString(HELLO_WORLD));      }   }



HelloWorld.properties:

HELLO_WORLD : Hello, eclipse world!

这样我们把要翻译的字符串装properties里面,就可以进行翻译了.

如何用resource bundle做i18n, 有很多文章有介绍, 大家如果不清楚, 可以上网查阅相
关文章.

2. 翻译字符串

这个靠的就不是技术了, 而是要靠翻译了. 对于不同的翻译来说, 它会根据properties
来翻译出你想要的本地语言

比如,一个中文翻译

它拿到properties文件后,会翻译成这样


HELLO_WORLD: 你好, eclipse世界!

并且写在helloworld_zh.properties文件中(zh - "中文"的英文所写)

这还没好, 由于eclipse运行机制问题,我们还要对这个文件转一下

就是把unicode字符转成acsii字符,

比如"你好,eclipse世界!" 应转成 "/u4f60/u597d,eclipse/u4e16/u754c!"

转换的工具为native2ascii.exe, 在jdk里面

这样, 一个语言的properties文件才算编辑完成

3. 创建片段

这里要介绍一个新的概念,就是片段, 它不是完整的插件, 而是某个插件的一部分. 而我
们一般的解决方案是把这些

translation放在片段中, 而主插件负责功能的实现即可.
所以你装了translation 包后,你会发觉你有很多这样的插件,名字叫xxx.xxx.xxx_1.0.
0
而且肯定还附带一个xxx.xxx.xxx.nl1_1.0.0, 这个就是对应插件的片段,用来存放翻译
信息.

注意: 创建片段的话,可不是用plugin.xml,而是用fragment.xml

我们看一下org.eclipse.ui这个插件的翻译包片段

id="org.eclipse.ui.nl1"
version="2.1.2"
plugin-id="org.eclipse.ui"
plugin-version="2.1.1">





这里定义了这个片段的name和id,已经它所属的plugin-id,还有翻译的信息运行库nl1.jar

4. 测试

创建好了片段,就要开始进行测试, 这个测试主要还是语言方面的检测,语义核对之类的工作.

当然, 说句老实话, 我们今天也只是了解一下这个技术, 做插件的国际化的技术一点都不难, 难就难在你要翻译这么多

东西, 而且还要翻译的对,翻译的好,让各国的人都用的爽,这个就难了, 估计也只有像IBM这样的大公司才有本事做好这个工作,呵呵. 自己开发么...呵呵, 能弄个中英文版已经很不错了

原创粉丝点击