Modified: 
ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/entity/src/org/ofbiz/entity/util/SequenceUtil.java
URL: 
http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/entity/src/org/ofbiz/entity/util/SequenceUtil.java?rev=1621696&r1=1621695&r2=1621696&view=diff
==============================================================================
--- 
ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/entity/src/org/ofbiz/entity/util/SequenceUtil.java
 (original)
+++ 
ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/entity/src/org/ofbiz/entity/util/SequenceUtil.java
 Mon Sep  1 07:29:23 2014
@@ -22,14 +22,12 @@ import java.sql.Connection;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
-import java.util.Hashtable;
-import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
 
 import javax.transaction.Transaction;
 
 import org.ofbiz.base.util.Debug;
-import org.ofbiz.base.util.UtilProperties;
-import org.ofbiz.entity.GenericDelegator;
 import org.ofbiz.entity.GenericEntityException;
 import org.ofbiz.entity.datasource.GenericHelperInfo;
 import org.ofbiz.entity.model.ModelEntity;
@@ -40,20 +38,18 @@ import org.ofbiz.entity.transaction.Tran
 
 /**
  * Sequence Utility to get unique sequences from named sequence banks
- * Uses a collision detection approach to safely get unique sequenced ids in 
banks from the database
  */
 public class SequenceUtil {
 
     public static final String module = SequenceUtil.class.getName();
 
-    private final Map<String, SequenceBank> sequences = new Hashtable<String, 
SequenceBank>();
+    private final ConcurrentMap<String, SequenceBank> sequences = new 
ConcurrentHashMap<String, SequenceBank>();
     private final GenericHelperInfo helperInfo;
     private final String tableName;
     private final String nameColName;
     private final String idColName;
-    private final boolean clustered;
 
-    public SequenceUtil(GenericDelegator delegator, GenericHelperInfo 
helperInfo, ModelEntity seqEntity, String nameFieldName, String idFieldName) {
+    public SequenceUtil(GenericHelperInfo helperInfo, ModelEntity seqEntity, 
String nameFieldName, String idFieldName) {
         this.helperInfo = helperInfo;
         if (seqEntity == null) {
             throw new IllegalArgumentException("The sequence model entity was 
null but is required.");
@@ -73,7 +69,6 @@ public class SequenceUtil {
             throw new IllegalArgumentException("Could not find the field 
definition for the sequence id field " + idFieldName);
         }
         this.idColName = idField.getColName();
-        clustered = delegator.useDistributedCacheClear() || 
"Y".equals(UtilProperties.getPropertyValue("general.properties", "clustered")); 
               
     }
 
     public Long getNextSeqId(String seqName, long staggerMax, ModelEntity 
seqModelEntity) {
@@ -95,18 +90,14 @@ public class SequenceUtil {
         SequenceBank bank = sequences.get(seqName);
 
         if (bank == null) {
-            synchronized(this) {
-                bank = sequences.get(seqName);
-                if (bank == null) {
-                    long bankSize = SequenceBank.defaultBankSize;
-                    if (seqModelEntity != null && 
seqModelEntity.getSequenceBankSize() != null) {
-                        bankSize = 
seqModelEntity.getSequenceBankSize().longValue();
-                        if (bankSize > SequenceBank.maxBankSize) bankSize = 
SequenceBank.maxBankSize;
-                    }
-                    bank = new SequenceBank(seqName, bankSize);
-                    sequences.put(seqName, bank);
-                }
+            long bankSize = SequenceBank.defaultBankSize;
+            if (seqModelEntity != null && seqModelEntity.getSequenceBankSize() 
!= null) {
+                bankSize = seqModelEntity.getSequenceBankSize().longValue();
+                if (bankSize > SequenceBank.maxBankSize) bankSize = 
SequenceBank.maxBankSize;
             }
+            bank = new SequenceBank(seqName, bankSize);
+            SequenceBank bankFromCache = sequences.putIfAbsent(seqName, bank);
+            bank = bankFromCache != null ? bankFromCache : bank;
         }
 
         return bank;
@@ -116,55 +107,55 @@ public class SequenceUtil {
         public static final long defaultBankSize = 10;
         public static final long maxBankSize = 5000;
         public static final long startSeqId = 10000;
-        public static final long minWaitMillis = 5;
-        public static final long maxWaitMillis = 50;
-        public static final int maxTries = 5;
 
-        private long curSeqId;
-        private long maxSeqId;
         private final String seqName;
         private final long bankSize;
+        private final String updateForLockStatement;
+        private final String selectSequenceStatement;
+
+        private long curSeqId;
+        private long maxSeqId;
 
         private SequenceBank(String seqName, long bankSize) {
             this.seqName = seqName;
             curSeqId = 0;
             maxSeqId = 0;
             this.bankSize = bankSize;
-            fillBank(1);
+            updateForLockStatement = "UPDATE " + SequenceUtil.this.tableName + 
" SET " + SequenceUtil.this.idColName + "=" + SequenceUtil.this.idColName + " 
WHERE " + SequenceUtil.this.nameColName + "='" + this.seqName + "'";
+            selectSequenceStatement = "SELECT " + SequenceUtil.this.idColName 
+ " FROM " + SequenceUtil.this.tableName + " WHERE " + 
SequenceUtil.this.nameColName + "='" + this.seqName + "'";
         }
 
-        private synchronized Long getNextSeqId(long staggerMax) {
+        private Long getNextSeqId(long staggerMax) {
             long stagger = 1;
             if (staggerMax > 1) {
-                stagger = Math.round(Math.random() * staggerMax);
+                stagger = (long)Math.ceil(Math.random() * staggerMax);
                 if (stagger == 0) stagger = 1;
             }
-
-            if ((curSeqId + stagger) <= maxSeqId) {
-                Long retSeqId = Long.valueOf(curSeqId);
-                curSeqId += stagger;
-                return retSeqId;
-            } else {
-                fillBank(stagger);
+            synchronized (this) {
                 if ((curSeqId + stagger) <= maxSeqId) {
-                    Long retSeqId = Long.valueOf(curSeqId);
+                    long retSeqId = curSeqId;
                     curSeqId += stagger;
                     return retSeqId;
                 } else {
-                    Debug.logError("[SequenceUtil.SequenceBank.getNextSeqId] 
Fill bank failed, returning null", module);
-                    return null;
+                    fillBank(stagger);
+                    if ((curSeqId + stagger) <= maxSeqId) {
+                        long retSeqId = curSeqId;
+                        curSeqId += stagger;
+                        return retSeqId;
+                    } else {
+                        Debug.logError("Fill bank failed, returning null", 
module);
+                        return null;
+                    }
                 }
             }
         }
 
-        private void refresh(long staggerMax) {
+        private synchronized void refresh(long staggerMax) {
             this.curSeqId = this.maxSeqId;
             this.fillBank(staggerMax);
         }
 
-        private synchronized void fillBank(long stagger) {
-            //Debug.logWarning("[SequenceUtil.SequenceBank.fillBank] Starting 
fillBank Thread Name is: " + Thread.currentThread().getName() + ":" + 
Thread.currentThread().toString(), module);
-
+        private void fillBank(long stagger) {
             // no need to get a new bank, SeqIds available
             if ((curSeqId + stagger) <= maxSeqId) return;
 
@@ -176,178 +167,117 @@ public class SequenceUtil {
 
             if (bankSize > maxBankSize) bankSize = maxBankSize;
 
-            long val1 = 0;
-            long val2 = 0;
+            Transaction suspendedTransaction = null;
+            try {
+                suspendedTransaction = TransactionUtil.suspend();
+
+                boolean beganTransaction = false;
+                try {
+                    beganTransaction = TransactionUtil.begin();
+
+                    Connection connection = null;
+                    Statement stmt = null;
+                    ResultSet rs = null;
 
-            // NOTE: the fancy ethernet type stuff is for the case where 
transactions not available, or something funny happens with really sensitive 
timing (between first select and update, for example)
-            int numTries = 0;
-
-            while (val1 + bankSize != val2) {
-                if (Debug.verboseOn()) 
Debug.logVerbose("[SequenceUtil.SequenceBank.fillBank] Trying to get a bank of 
sequenced ids for " +
-                        this.seqName + "; start of loop val1=" + val1 + ", 
val2=" + val2 + ", bankSize=" + bankSize, module);
-
-                // not sure if this synchronized block is totally necessary, 
the method is synchronized but it does do a wait/sleep
-                // outside of this block, and this is the really sensitive 
block, so making sure it is isolated; there is some overhead
-                // to this, but very bad things can happen if we try to do too 
many of these at once for a single sequencer
-                synchronized (this) {
-                    Transaction suspendedTransaction = null;
                     try {
-                        //if we can suspend the transaction, we'll try to do 
this in a local manual transaction
-                        suspendedTransaction = TransactionUtil.suspend();
-
-                        boolean beganTransaction = false;
-                        try {
-                            beganTransaction = TransactionUtil.begin();
-
-                            Connection connection = null;
-                            Statement stmt = null;
-                            ResultSet rs = null;
-
-                            try {
-                                connection = 
TransactionFactoryLoader.getInstance().getConnection(SequenceUtil.this.helperInfo);
-                            } catch (SQLException sqle) {
-                                
Debug.logWarning("[SequenceUtil.SequenceBank.fillBank]: Unable to esablish a 
connection with the database... Error was:" + sqle.toString(), module);
-                                throw sqle;
-                            } catch (GenericEntityException e) {
-                                
Debug.logWarning("[SequenceUtil.SequenceBank.fillBank]: Unable to esablish a 
connection with the database... Error was: " + e.toString(), module);
-                                throw e;
-                            }
-
-                            if (connection == null) {
-                                throw new 
GenericEntityException("[SequenceUtil.SequenceBank.fillBank]: Unable to 
esablish a connection with the database, connection was null...");
-                            }
-
-                            String sql = null;
-
-                            try {
-                                // we shouldn't need this, and some TX 
managers complain about it, so not including it: 
connection.setAutoCommit(false);
-
-                                stmt = connection.createStatement();
-                                if (clustered) {
-                                    sql = "SELECT " + 
SequenceUtil.this.idColName + " FROM " + SequenceUtil.this.tableName + " WHERE 
" + SequenceUtil.this.nameColName + "='" + this.seqName + "'" + " FOR UPDATE";  
                                  
-                                } else {
-                                    sql = "SELECT " + 
SequenceUtil.this.idColName + " FROM " + SequenceUtil.this.tableName + " WHERE 
" + SequenceUtil.this.nameColName + "='" + this.seqName + "'";
-                                }
-                                rs = stmt.executeQuery(sql);
-                                boolean gotVal1 = false;
-                                if (rs.next()) {
-                                    val1 = 
rs.getLong(SequenceUtil.this.idColName);
-                                    gotVal1 = true;
-                                }
-                                rs.close();
-
-                                if (!gotVal1) {
-                                    
Debug.logWarning("[SequenceUtil.SequenceBank.fillBank] first select failed: 
will try to add new row, result set was empty for sequence [" + seqName + "] 
\nUsed SQL: " + sql + " \n Thread Name is: " + Thread.currentThread().getName() 
+ ":" + Thread.currentThread().toString(), module);
-                                    sql = "INSERT INTO " + 
SequenceUtil.this.tableName + " (" + SequenceUtil.this.nameColName + ", " + 
SequenceUtil.this.idColName + ") VALUES ('" + this.seqName + "', " + startSeqId 
+ ")";
-                                    if (stmt.executeUpdate(sql) <= 0) {
-                                        throw new GenericEntityException("No 
rows changed when trying insert new sequence row with this SQL: " + sql);
-                                    }
-                                    continue;
-                                }
-
-                                sql = "UPDATE " + SequenceUtil.this.tableName 
+ " SET " + SequenceUtil.this.idColName + "=" + SequenceUtil.this.idColName + 
"+" + bankSize + " WHERE " + SequenceUtil.this.nameColName + "='" + 
this.seqName + "'";
-                                if (stmt.executeUpdate(sql) <= 0) {
-                                    throw new 
GenericEntityException("[SequenceUtil.SequenceBank.fillBank] update failed, no 
rows changes for seqName: " + seqName);
-                                }
-                                if (clustered) {
-                                    sql = "SELECT " + 
SequenceUtil.this.idColName + " FROM " + SequenceUtil.this.tableName + " WHERE 
" + SequenceUtil.this.nameColName + "='" + this.seqName + "'" + " FOR UPDATE";  
                                  
-
-                                } else {
-                                    sql = "SELECT " + 
SequenceUtil.this.idColName + " FROM " + SequenceUtil.this.tableName + " WHERE 
" + SequenceUtil.this.nameColName + "='" + this.seqName + "'";
-                                }
-                                rs = stmt.executeQuery(sql);
-                                boolean gotVal2 = false;
-                                if (rs.next()) {
-                                    val2 = 
rs.getLong(SequenceUtil.this.idColName);
-                                    gotVal2 = true;
-                                }
-
-                                rs.close();
+                        connection = 
TransactionFactoryLoader.getInstance().getConnection(SequenceUtil.this.helperInfo);
+                    } catch (SQLException sqle) {
+                        Debug.logWarning("Unable to esablish a connection with 
the database. Error was:" + sqle.toString(), module);
+                        throw sqle;
+                    } catch (GenericEntityException e) {
+                        Debug.logWarning("Unable to esablish a connection with 
the database. Error was: " + e.toString(), module);
+                        throw e;
+                    }
+                    if (connection == null) {
+                        throw new GenericEntityException("Unable to esablish a 
connection with the database, connection was null...");
+                    }
 
-                                if (!gotVal2) {
-                                    throw new 
GenericEntityException("[SequenceUtil.SequenceBank.fillBank] second select 
failed: aborting, result set was empty for sequence: " + seqName);
-                                }
+                    String sql = null;
+                    try {
+                        stmt = connection.createStatement();
 
-                                // got val1 and val2 at this point, if we 
don't have the right difference between them, force a rollback (with
-                                //setRollbackOnly and NOT with an exception 
because we don't want to break from the loop, just err out and
-                                //continue), then flow out to allow the wait 
and loop thing to happen
-                                if (val1 + bankSize != val2) {
-                                    TransactionUtil.setRollbackOnly("Forcing 
transaction rollback in sequence increment because we didn't get a clean 
update, ie a conflict was found, so not saving the results", null);
-                                }
-                            } catch (SQLException sqle) {
-                                Debug.logWarning(sqle, 
"[SequenceUtil.SequenceBank.fillBank] SQL Exception while executing the 
following:\n" + sql + "\nError was:" + sqle.getMessage(), module);
-                                throw sqle;
-                            } finally {
-                                try {
-                                    if (stmt != null) stmt.close();
-                                } catch (SQLException sqle) {
-                                    Debug.logWarning(sqle, "Error closing 
statement in sequence util", module);
-                                }
-                                try {
-                                    if (connection != null) connection.close();
-                                } catch (SQLException sqle) {
-                                    Debug.logWarning(sqle, "Error closing 
connection in sequence util", module);
+                        // run an update with no changes to get a lock on the 
record
+                        if (stmt.executeUpdate(updateForLockStatement) <= 0) {
+                            Debug.logWarning("First select failed: will try to 
add new row, result set was empty for sequence [" + seqName + "] \nUsed SQL: " 
+ sql + " \n ", module);
+                            sql = "INSERT INTO " + SequenceUtil.this.tableName 
+ " (" + SequenceUtil.this.nameColName + ", " + SequenceUtil.this.idColName + 
") VALUES ('" + this.seqName + "', " + startSeqId + ")";
+                            if (stmt.executeUpdate(sql) <= 0) {
+                                // insert failed: this means that another 
thread inserted the record; then retry to run an update with no changes to get 
a lock on the record
+                                if (stmt.executeUpdate(updateForLockStatement) 
<= 0) {
+                                    // This should never happen
+                                    throw new GenericEntityException("No rows 
changed when trying insert new sequence row with this SQL: " + sql);
                                 }
                             }
-                        } catch (Exception e) {
-                            String errMsg = "General error in getting a 
sequenced ID";
-                            Debug.logError(e, errMsg, module);
-                            try {
-                                TransactionUtil.rollback(beganTransaction, 
errMsg, e);
-                            } catch (GenericTransactionException gte2) {
-                                Debug.logError(gte2, "Unable to rollback 
transaction", module);
-                            }
-
-                            // error, break out of the loop to not try to 
continue forever
-                            break;
-                        } finally {
-                            try {
-                                TransactionUtil.commit(beganTransaction);
-                            } catch (GenericTransactionException gte) {
-                                Debug.logError(gte, "Unable to commit sequence 
increment transaction, continuing anyway though", module);
-                            }
                         }
-                    } catch (GenericTransactionException e) {
-                        Debug.logError(e, "System Error suspending transaction 
in sequence util", module);
+                        // select the record (now locked) to get the curSeqId
+                        rs = stmt.executeQuery(selectSequenceStatement);
+                        boolean gotVal = false;
+                        if (rs.next()) {
+                            curSeqId = rs.getLong(SequenceUtil.this.idColName);
+                            gotVal = true;
+                        }
+                        rs.close();
+                        if (!gotVal) {
+                            throw new GenericEntityException("Failed to find 
the sequence record: " + sql);
+                        }
+                        // increment the sequence
+                        sql = "UPDATE " + SequenceUtil.this.tableName + " SET 
" + SequenceUtil.this.idColName + "=" + SequenceUtil.this.idColName + "+" + 
bankSize + " WHERE " + SequenceUtil.this.nameColName + "='" + this.seqName + 
"'";
+                        if (stmt.executeUpdate(sql) <= 0) {
+                            throw new GenericEntityException("Update failed, 
no rows changes for seqName: " + seqName);
+                        }
+
+                        TransactionUtil.commit(beganTransaction);
+
+                    } catch (SQLException sqle) {
+                        Debug.logWarning(sqle, "SQL Exception:" + 
sqle.getMessage(), module);
+                        throw sqle;
                     } finally {
-                        if (suspendedTransaction != null) {
-                            try {
-                                TransactionUtil.resume(suspendedTransaction);
-                            } catch (GenericTransactionException e) {
-                                Debug.logError(e, "Error resuming suspended 
transaction in sequence util", module);
-                            }
+                        try {
+                            if (stmt != null) stmt.close();
+                        } catch (SQLException sqle) {
+                            Debug.logWarning(sqle, "Error closing statement in 
sequence util", module);
+                        }
+                        try {
+                            if (connection != null) connection.close();
+                        } catch (SQLException sqle) {
+                            Debug.logWarning(sqle, "Error closing connection 
in sequence util", module);
                         }
                     }
-                }
-
-                if (val1 + bankSize != val2) {
-                    if (numTries >= maxTries) {
-                        String errMsg = "[SequenceUtil.SequenceBank.fillBank] 
maxTries (" + maxTries + ") reached for seqName [" + this.seqName + "], giving 
up.";
-                        Debug.logError(errMsg, module);
-                        return;
+                } catch (Exception e) {
+                    // reset the sequence fields and return (note: it would be 
better to throw an exception)
+                    curSeqId = 0;
+                    maxSeqId = 0;
+                    String errMsg = "General error in getting a sequenced ID";
+                    Debug.logError(e, errMsg, module);
+                    try {
+                        TransactionUtil.rollback(beganTransaction, errMsg, e);
+                    } catch (GenericTransactionException gte2) {
+                        Debug.logError(gte2, "Unable to rollback transaction", 
module);
                     }
-
-                    // collision happened, wait a bounded random amount of 
time then continue
-                    long waitTime = (long) (Math.random() * (maxWaitMillis - 
minWaitMillis) + minWaitMillis);
-
-                    Debug.logWarning("[SequenceUtil.SequenceBank.fillBank] 
Collision found for seqName [" + seqName + "], val1=" + val1 + ", val2=" + val2 
+ ", val1+bankSize=" + (val1 + bankSize) + ", bankSize=" + bankSize + ", 
waitTime=" + waitTime, module);
-
+                    return;
+                }
+            } catch (GenericTransactionException e) {
+                Debug.logError(e, "System Error suspending transaction in 
sequence util", module);
+                // reset the sequence fields and return (note: it would be 
better to throw an exception)
+                curSeqId = 0;
+                maxSeqId = 0;
+                return;
+            } finally {
+                if (suspendedTransaction != null) {
                     try {
-                        // using the Thread.sleep to more reliably lock this 
thread: this.wait(waitTime);
-                        java.lang.Thread.sleep(waitTime);
-                    } catch (Exception e) {
-                        Debug.logWarning(e, "Error waiting in sequence util", 
module);
+                        TransactionUtil.resume(suspendedTransaction);
+                    } catch (GenericTransactionException e) {
+                        Debug.logError(e, "Error resuming suspended 
transaction in sequence util", module);
+                        // reset the sequence fields and return (note: it 
would be better to throw an exception)
+                        curSeqId = 0;
+                        maxSeqId = 0;
                         return;
                     }
                 }
-
-                numTries++;
             }
 
-            curSeqId = val1;
-            maxSeqId = val2;
+            maxSeqId = curSeqId + bankSize;
             if (Debug.infoOn()) Debug.logInfo("Got bank of sequenced IDs for 
[" + this.seqName + "]; curSeqId=" + curSeqId + ", maxSeqId=" + maxSeqId + ", 
bankSize=" + bankSize, module);
-            //Debug.logWarning("[SequenceUtil.SequenceBank.fillBank] Ending 
fillBank Thread Name is: " + Thread.currentThread().getName() + ":" + 
Thread.currentThread().toString(), module);
         }
     }
 }
+

Modified: 
ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/entityext/config/EntityExtUiLabels.xml
URL: 
http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/entityext/config/EntityExtUiLabels.xml?rev=1621696&r1=1621695&r2=1621696&view=diff
==============================================================================
--- 
ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/entityext/config/EntityExtUiLabels.xml
 (original)
+++ 
ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/entityext/config/EntityExtUiLabels.xml
 Mon Sep  1 07:29:23 2014
@@ -24,119 +24,139 @@
         <value xml:lang="fr">Impossible d'effectuer la synchro (push) avec un 
configuration en mode esclave seulement (pull only).</value>
         <value xml:lang="it">Non è possibile fare Entity Sync Push perchè 
entitySyncId [] è impostato in Pull Only.</value>
         <value xml:lang="zh">无法实现实体同步推送因为实体同步æ 
‡è¯†ï¼ˆentitySyncId [])被设置为了只能提取。</value>
+        <value xml:lang="zh_TW">無法進行個體同步推送因
為個體同步代號(entitySyncId [])被設定為 Pull Only。</value>
     </property>
     <property key="EntityExtCannotFindDelegator">
         <value xml:lang="en">Could not find delegator with specified name 
${overrideDelegatorName}</value>
         <value xml:lang="fr">Le delegateur avec le nom 
${overrideDelegatorName} n'a pu être trouvé</value>
         <value xml:lang="it">Could not find delegator with specified name 
${overrideDelegatorName}</value>
         <value xml:lang="zh">无
法找到名称为${overrideDelegatorName}的代理</value>
+        <value 
xml:lang="zh_TW">無法找到名稱為${overrideDelegatorName}的代理</value>
     </property>
     <property key="EntityExtEntitySyncXMLDocumentIsNotValid">
         <value xml:lang="en">EntitySync XML document ${fileName} is not 
valid!</value>
         <value xml:lang="fr">L'EntitySync XML document ${fileName} n'est pas 
valide !</value>
         <value xml:lang="it">EntitySync XML documento ${fileName} non è 
valido!</value>
         <value 
xml:lang="zh">实体同步(EntitySync)XML文档${fileName}无效!</value>
+        <value 
xml:lang="zh_TW">個体同步(EntitySync)XML文件${fileName}無效!</value>
     </property>
     <property key="EntityExtErrorCallingRemotePull">
         <value xml:lang="en">Error calling remote pull and report EntitySync 
service with name: ${remotePullAndReportEntitySyncDataName}</value>
         <value xml:lang="fr">Erreur lors de l'appel de l'esclave (remote pull) 
et de son rapport sur le service nommé : 
${remotePullAndReportEntitySyncDataName}</value>
         <value xml:lang="it">Errore durante la chiamata del pull remoto e 
report EntitySync servizio con il nome: 
${remotePullAndReportEntitySyncDataName}</value>
         <value 
xml:lang="zh">调用远程提取和报表的实体同步(EntitySync)服务时出错,服务名称:${remotePullAndReportEntitySyncDataName}</value>
+        <value xml:lang="zh_TW">呼叫遠程提取和å 
±è¡¨çš„個体同步(EntitySync)服務時出錯,服務名稱:${remotePullAndReportEntitySyncDataName}</value>
     </property>
     <property key="EntityExtErrorCallingService">
         <value xml:lang="en">Error calling service to store data 
locally</value>
         <value xml:lang="fr">Erreur d'appel du service de mise à jour des 
données locales</value>
         <value xml:lang="it">Errore durante la chiamata del servizio di 
registrazione locale dei dati</value>
         <value xml:lang="zh">调用服务来本地存储数据时出错</value>
+        <value 
xml:lang="zh_TW">呼叫服務來本地存儲資料時出錯</value>
     </property>
     <property key="EntityExtErrorCleaningEntitySyncRemove">
         <value xml:lang="en">Error cleaning out EntitySyncRemove info: 
${errorString}</value>
         <value xml:lang="fr">Erreur lors du nettoyage des EntitySyncRemove, 
plus d'informations : ${errorString}</value>
         <value xml:lang="it">Error cleaning out EntitySyncRemove info: 
${errorString}</value>
         <value xml:lang="zh">清除实体同步删
除(EntitySyncRemove)信息时出错:${errorString}</value>
+        <value xml:lang="zh_TW">清除個體同步删
除(EntitySyncRemove)訊息時出錯:${errorString}</value>
     </property>
     <property key="EntityExtErrorGettingListOfEntityInGroup">
         <value xml:lang="en">Error getting list of entities in group: 
${errorString}</value>
         <value xml:lang="fr">Erreur lors de la récupération des entités du 
group : ${errorString}</value>
         <value xml:lang="it">Errore ad ottenere la lista delle entità nel 
gruppo: ${errorString}</value>
         <value 
xml:lang="zh">获取分组中的实体列表时出错:${errorString}</value>
+        <value 
xml:lang="zh_TW">取得分组中的個體列表時出錯:${errorString}</value>
     </property>
     <property key="EntityExtErrorUnwrappingRecords">
         <value xml:lang="en">Error unwrapping ByteWrapper records: 
${errorString}</value>
         <value xml:lang="fr">Erreur lors de l"ouverture du flux des 
enregistrements : ${errorString}</value>
         <value xml:lang="it">Errore unwrapping ByteWrapper records: 
${errorString}</value>
         <value 
xml:lang="zh">解析ByteWrapper记录时出错:${errorString}</value>
+        <value 
xml:lang="zh_TW">解析ByteWrapper記錄時出錯:${errorString}</value>
     </property>
     <property key="EntityExtErrorSavingEntitySyncData">
         <value xml:lang="en">Error saving Entity Sync Data for entitySyncId 
${entitySyncId}: ${errorString}</value>
         <value xml:lang="fr">Erreur lors de la synchro de donnée pour la 
référence ${entitySyncId}: ${errorString}</value>
         <value xml:lang="it">Errore durante il salvataggio Entity Sync Data 
per entitySyncId ${entitySyncId}: ${errorString}</value>
         <value xml:lang="zh">为实体同步æ 
‡è¯†ï¼ˆentitySyncId)${entitySyncId}保存实体同步数据时出错:${errorString}</value>
+        <value 
xml:lang="zh_TW">為個體同步代號(entitySyncId)${entitySyncId}保存個體同步資料時出錯:${errorString}</value>
     </property>
     <property key="EntityExtExceptionSavingEntitySyncData">
         <value xml:lang="en">Exception saving Entity Sync Data for 
entitySyncId ${entitySyncId}: ${errorString}</value>
         <value xml:lang="fr">Exception lors de l'enregistrement de la synchro 
de donnée pour la référence ${entitySyncId}: ${errorString}</value>
         <value xml:lang="it">Eccezione durante il salvataggio Entity Sync Data 
per entitySyncId ${entitySyncId}: ${errorString}</value>
         <value xml:lang="zh">为实体同步æ 
‡è¯†ï¼ˆentitySyncId)${entitySyncId}保存实体同步数据时出现意外:${errorString}</value>
+        <value 
xml:lang="zh_TW">為個體同步代號(entitySyncId)${entitySyncId}保存個體同步資料時出現異常:${errorString}</value>
     </property>
     <property key="EntityExtFileNotFound">
         <value xml:lang="en">File not found: ${fileName}</value>
         <value xml:lang="fr">Fichier ${fileName} non trouvé</value>
         <value xml:lang="it">File non trovato: ${fileName}</value>
         <value xml:lang="zh">没有找到文件:${fileName}</value>
+        <value xml:lang="zh_TW">没有找到文件:${fileName}</value>
     </property>
     <property key="EntityExtNoFileAvailableInTheRootDirectory">
         <value xml:lang="en">No files available for reading in this root 
directory: ${rootDirectory}</value>
         <value xml:lang="fr">Aucun fichier disponible pour la lecture dans le 
répertoire ${rootDirectory}</value>
         <value xml:lang="it">Nessun file disponibile da leggere nella 
directory principale: ${rootDirectory}</value>
         <value xml:lang="zh">这个æ 
¹ç›®å½•下没有可读的文件:${rootDirectory}</value>
+        <value xml:lang="zh_TW">這個æ 
¹ç›®éŒ„下没有可讀的文件:${rootDirectory}</value>
     </property>
     <property key="EntityExtOfflineXMLFileNotFound">
         <value xml:lang="en">Offline EntitySync XML file not found 
(${fileName})</value>
         <value xml:lang="fr">Le fichier hors ligne (${fileName}) n'a pas été 
trouvé</value>
         <value xml:lang="it">File Offline EntitySync XML non trovato 
(${fileName})</value>
         <value 
xml:lang="zh">没有找到离线实体同步(EntitySync)XML文件(${fileName})</value>
+        <value 
xml:lang="zh_TW">没有找到離線個體同步(EntitySync)XML文件(${fileName})</value>
     </property>
     <property key="EntityExtProblemReadingFile">
         <value xml:lang="en">Problem reading file: ${fileName}</value>
         <value xml:lang="fr">Problème lors de la lecture du fichier 
${fileName}</value>
         <value xml:lang="it">Problema durante la lettura del file: 
${fileName}</value>
         <value xml:lang="zh">读文件时出问题:${fileName}</value>
+        <value xml:lang="zh_TW">讀文件時出問題:${fileName}</value>
     </property>
     <property key="EntityExtRootDirectoryDoesNotExists">
         <value xml:lang="en">Root directory does not exist or is not 
readable.</value>
         <value xml:lang="fr">Le répertoire racine n'existe pas ou n'est pas 
lisible</value>
         <value xml:lang="it">Directory principale non esiste o non è 
leggibile.</value>
         <value xml:lang="zh">根目录不存在或不可读。</value>
+        <value xml:lang="zh_TW">根目錄不存在或不可讀。</value>
     </property>
     <property key="EntityExtServicePermissionNotGranted">
         <value xml:lang="en">You do not have permission to run this 
service.</value>
         <value xml:lang="fr">Vous n'avez pas la permission d'exécuter ce 
service</value>
         <value xml:lang="it">Tu non hai il permesso di eseguire questo 
servizio.</value>
         <value xml:lang="zh">你没有权限运行这个服务。</value>
+        <value xml:lang="zh_TW">你没有權限執行這個服務。</value>
     </property>
     <property key="EntityExtThisServiceIsNotYetImplemented">
         <value xml:lang="en">This service is not implemented yet.</value>
         <value xml:lang="fr">Ce service n'est pas implémenté 
actuellement</value>
         <value xml:lang="it">Questo servizio non è ancora 
implementato.</value>
         <value xml:lang="zh">这个服务尚未实现。</value>
+        <value xml:lang="zh_TW">這個服務尚未實作。</value>
     </property>
     <property key="EntityExtUnableToLoadXMLDocument">
         <value xml:lang="en">Unable to load EntitySync XML ${entitySyncId} - 
Problem at '${startTime}' Error: ${errorString}</value>
         <value xml:lang="fr">Impossible de charger l'EntitySync XML 
${entitySyncId} - Problème à '${startTime}' , avec l'erreur suivante : 
${errorString}</value>
         <value xml:lang="it">Non è possibile caricare il EntitySync XML 
${entitySyncId} - Problema alle '${startTime}' Errore: ${errorString}</value>
         <value xml:lang="zh">无法载入实体同步(EntitySync)XML 
${entitySyncId} - 
问题发生在'${startTime}',错误:${errorString}</value>
+        <value xml:lang="zh_TW">無法載入個體同步(EntitySync)XML 
${entitySyncId} - 
問題發生在'${startTime}',錯誤:${errorString}</value>
     </property>
     <property key="EntityExtUnableToLocateRootDirectory">
         <value xml:lang="en">Unable to locate root directory 
${rootDirectory}</value>
         <value xml:lang="fr">Impossible de localiser le répertoire racine 
${rootDirectory}</value>
         <value xml:lang="it">Non è possibile localizzare la directory 
principale ${rootDirectory}</value>
         <value xml:lang="zh">无法定位根目录${rootDirectory}</value>
+        <value xml:lang="zh_TW">無法定位根目錄${rootDirectory}</value>
     </property>
     <property key="EntityExtUnableToLocateRootDirectoryURI">
         <value xml:lang="en">Unable to get root directory URI</value>
         <value xml:lang="fr">Impossible de récupérer le répertoire 
racine</value>
         <value xml:lang="it">Non è possibile localizzare la directory 
principale URI</value>
         <value xml:lang="zh">不能得到根目录URI</value>
+        <value xml:lang="zh_TW">不能取得根目錄URI</value>
     </property>
 </resource>

Modified: 
ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/minilang/config/DefaultMessages.xml
URL: 
http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/minilang/config/DefaultMessages.xml?rev=1621696&r1=1621695&r2=1621696&view=diff
==============================================================================
--- 
ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/minilang/config/DefaultMessages.xml
 (original)
+++ 
ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/minilang/config/DefaultMessages.xml
 Mon Sep  1 07:29:23 2014
@@ -26,6 +26,7 @@
         <value xml:lang="ja">エラー:</value>
         <value xml:lang="nl">Fout: </value>     
         <value xml:lang="zh">错误:</value>
+        <value xml:lang="zh_TW">錯誤:</value>
     </property>
     <property key="check.error.suffix">
         <value xml:lang="en">&#160;</value>
@@ -58,6 +59,7 @@
         <value xml:lang="ja">エラー:</value>
         <value xml:lang="nl">Fout: </value>     
         <value xml:lang="zh">错误:</value>
+        <value xml:lang="zh_TW">錯誤:</value>
     </property>
     <property key="service.error.suffix">
         <value xml:lang="en">&#160;</value>

Modified: 
ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/minilang/config/MiniLangErrorUiLabels.xml
URL: 
http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/minilang/config/MiniLangErrorUiLabels.xml?rev=1621696&r1=1621695&r2=1621696&view=diff
==============================================================================
--- 
ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/minilang/config/MiniLangErrorUiLabels.xml
 (original)
+++ 
ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/minilang/config/MiniLangErrorUiLabels.xml
 Mon Sep  1 07:29:23 2014
@@ -43,13 +43,14 @@
         <value xml:lang="ru">Ошибка выполнения 
simple-method</value>
         <value xml:lang="th">Error : ผิดพลาด ! 
ในการรัน simple-method</value>
         <value xml:lang="zh">运行这个简单方法时出错</value>
-        <value xml:lang="zh_TW">運行這個簡單方法時出錯</value>
+        <value xml:lang="zh_TW">執行這個簡單方法時出錯</value>
     </property>
     <property key="simpleMethod.error_show_service_name">
         <value xml:lang="en">calling service ${serviceName} in 
${methodName}</value>
         <value xml:lang="it">eseguendo il servizio ${serviceName} in 
${methodName}</value>
         <value xml:lang="ja">サービス ${serviceName} を ${methodName} 
で呼び出し</value>
         <value xml:lang="zh">在${methodName}中调用服务${serviceName} 
</value>
+        <value xml:lang="zh_TW">在${methodName}中呼叫服務${serviceName} 
</value>
     </property>
     <property key="simpleMethod.must_logged_process">
         <value xml:lang="de">Sie müssen am System angemeldet sein um 
[${shortDescription}] auszuführen</value>
@@ -62,6 +63,6 @@
         <value xml:lang="ru">Для завершения процесса 
${shortDescription} вы должны зарегистрироваться в 
системе</value>
         <value 
xml:lang="th">คุณต้องทำการเข้าสู่ระบบในการประมวลผล
  [${shortDescription}] ให้เสร็จสมบูรณ์</value>
         <value xml:lang="zh">你必
须登录才能完成${shortDescription}步骤</value>
-        <value xml:lang="zh_TW">你必é 
ˆç™»éŒ„才能完成${shortDescription}步驟</value>
+        <value xml:lang="zh_TW">你必須登å…
¥æ‰èƒ½å®Œæˆ${shortDescription}步驟</value>
     </property>
 </resource>

Modified: 
ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/minilang/src/org/ofbiz/minilang/SimpleMethod.java
URL: 
http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/minilang/src/org/ofbiz/minilang/SimpleMethod.java?rev=1621696&r1=1621695&r2=1621696&view=diff
==============================================================================
--- 
ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/minilang/src/org/ofbiz/minilang/SimpleMethod.java
 (original)
+++ 
ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/minilang/src/org/ofbiz/minilang/SimpleMethod.java
 Mon Sep  1 07:29:23 2014
@@ -24,6 +24,7 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.LinkedHashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
@@ -84,7 +85,7 @@ public final class SimpleMethod extends 
     private static final String[] DEPRECATED_ATTRIBUTES = 
{"parameter-map-name", "locale-name", "delegator-name", "security-name", 
"dispatcher-name", "user-login-name"};
     private static final Map<String, MethodOperation.Factory<MethodOperation>> 
methodOperationFactories;
     private static final UtilCache<String, Map<String, SimpleMethod>> 
simpleMethodsDirectCache = 
UtilCache.createUtilCache("minilang.SimpleMethodsDirect", 0, 0);
-    private static final UtilCache<String, Map<String, SimpleMethod>> 
simpleMethodsResourceCache = 
UtilCache.createUtilCache("minilang.SimpleMethodsResource", 0, 0);
+    private static final UtilCache<String, SimpleMethod> 
simpleMethodsResourceCache = 
UtilCache.createUtilCache("minilang.SimpleMethodsResource", 0, 0);
 
     static {
         Map<String, MethodOperation.Factory<MethodOperation>> mapFactories = 
new HashMap<String, MethodOperation.Factory<MethodOperation>>();
@@ -134,7 +135,7 @@ public final class SimpleMethod extends 
     }
 
     private static Map<String, SimpleMethod> getAllSimpleMethods(URL xmlURL) 
throws MiniLangException {
-        Map<String, SimpleMethod> simpleMethods = new HashMap<String, 
SimpleMethod>();
+        Map<String, SimpleMethod> simpleMethods = new LinkedHashMap<String, 
SimpleMethod>();
         Document document = null;
         try {
             document = UtilXml.readXmlDocument(xmlURL, true, true);
@@ -160,14 +161,31 @@ public final class SimpleMethod extends 
 
     public static SimpleMethod getSimpleMethod(String xmlResource, String 
methodName, ClassLoader loader) throws MiniLangException {
         Assert.notNull("methodName", methodName);
-        Map<String, SimpleMethod> simpleMethods = 
getSimpleMethods(xmlResource, loader);
-        return simpleMethods.get(methodName);
+        String key = xmlResource.concat("#").concat(methodName);
+        SimpleMethod method = simpleMethodsResourceCache.get(key);
+        if (method == null) {
+            Map<String, SimpleMethod> simpleMethods = 
getSimpleMethods(xmlResource, loader);
+            for (Map.Entry<String, SimpleMethod> entry : 
simpleMethods.entrySet()) {
+                String putKey = xmlResource.concat("#").concat(entry.getKey());
+                simpleMethodsResourceCache.putIfAbsent(putKey, 
entry.getValue());
+            }
+        }
+        return simpleMethodsResourceCache.get(key);
     }
 
     public static SimpleMethod getSimpleMethod(URL xmlUrl, String methodName) 
throws MiniLangException {
         Assert.notNull("methodName", methodName);
-        Map<String, SimpleMethod> simpleMethods = getSimpleMethods(xmlUrl);
-        return simpleMethods.get(methodName);
+        String xmlResource = xmlUrl.toString();
+        String key = xmlResource.concat("#").concat(methodName);
+        SimpleMethod method = simpleMethodsResourceCache.get(key);
+        if (method == null) {
+            Map<String, SimpleMethod> simpleMethods = 
getAllSimpleMethods(xmlUrl);
+            for (Map.Entry<String, SimpleMethod> entry : 
simpleMethods.entrySet()) {
+                String putKey = xmlResource.concat("#").concat(entry.getKey());
+                simpleMethodsResourceCache.putIfAbsent(putKey, 
entry.getValue());
+            }
+        }
+        return simpleMethodsResourceCache.get(key);
     }
 
     private static Map<String, SimpleMethod> getSimpleMethods(String 
xmlResource, ClassLoader loader) throws MiniLangException {
@@ -181,43 +199,23 @@ public final class SimpleMethod extends 
         if (xmlURL == null) {
             throw new MiniLangException("Could not find SimpleMethod XML 
document in resource: " + xmlResource);
         }
-        return getSimpleMethods(xmlURL);
-    }
-
-    private static Map<String, SimpleMethod> getSimpleMethods(URL xmlURL) 
throws MiniLangException {
-        Assert.notNull("xmlURL", xmlURL);
-        String cacheKey = xmlURL.toString();
-        Map<String, SimpleMethod> simpleMethods = 
simpleMethodsResourceCache.get(cacheKey);
-        if (simpleMethods == null) {
-            simpleMethods = getAllSimpleMethods(xmlURL);
-            simpleMethods = 
simpleMethodsResourceCache.putIfAbsentAndGet(cacheKey, simpleMethods);
-        }
-        return simpleMethods;
+        return getAllSimpleMethods(xmlURL);
     }
 
+    /**
+     * Returns a List of <code>SimpleMethod</code> objects compiled from 
<code>xmlResource</code>.
+     * The ordering in the List is the same as the XML file.
+     * <p>This method is used by unit test framework to run tests in the order 
they appear in the XML file.
+     * Method caching is bypassed since the methods are executed only once.</p>
+     * 
+     * @param xmlResource
+     * @param loader
+     * @return
+     * @throws MiniLangException
+     */
     public static List<SimpleMethod> getSimpleMethodsList(String xmlResource, 
ClassLoader loader) throws MiniLangException {
-        Assert.notNull("xmlResource", xmlResource);
-        List<SimpleMethod> simpleMethods = new ArrayList<SimpleMethod>();
-        // Let the standard Map returning method take care of caching and 
compilation
         Map<String, SimpleMethod> simpleMethodMap = 
getSimpleMethods(xmlResource, loader);
-        // Load and traverse the document again to get a correctly ordered 
list of methods
-        URL xmlURL = null;
-        try {
-            xmlURL = FlexibleLocation.resolveLocation(xmlResource, loader);
-        } catch (MalformedURLException e) {
-            throw new MiniLangException("Could not find SimpleMethod XML 
document in resource: " + xmlResource + ": ", e);
-        }
-        Document document = null;
-        try {
-            document = UtilXml.readXmlDocument(xmlURL, 
MiniLangValidate.validationOn(), true);
-        } catch (Exception e) {
-            throw new MiniLangException("Could not read SimpleMethod XML 
document [" + xmlURL + "]: ", e);
-        }
-        Element rootElement = document.getDocumentElement();
-        for (Element simpleMethodElement : 
UtilXml.childElementList(rootElement, "simple-method")) {
-            
simpleMethods.add(simpleMethodMap.get(simpleMethodElement.getAttribute("method-name")));
-        }
-        return simpleMethods;
+        return new ArrayList<SimpleMethod>(simpleMethodMap.values());
     }
 
     public static List<MethodOperation> readOperations(Element 
simpleMethodElement, SimpleMethod simpleMethod) throws MiniLangException {

Modified: 
ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/minilang/src/org/ofbiz/minilang/method/callops/CallSimpleMethod.java
URL: 
http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/minilang/src/org/ofbiz/minilang/method/callops/CallSimpleMethod.java?rev=1621696&r1=1621695&r2=1621696&view=diff
==============================================================================
--- 
ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/minilang/src/org/ofbiz/minilang/method/callops/CallSimpleMethod.java
 (original)
+++ 
ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/minilang/src/org/ofbiz/minilang/method/callops/CallSimpleMethod.java
 Mon Sep  1 07:29:23 2014
@@ -169,10 +169,6 @@ public final class CallSimpleMethod exte
         return this.methodName;
     }
 
-    public SimpleMethod getSimpleMethodToCall(ClassLoader loader) throws 
MiniLangException {
-        return SimpleMethod.getSimpleMethod(xmlResource, methodName, loader);
-    }
-
     public String getXmlResource() {
         return this.xmlResource;
     }

Modified: 
ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/resources/templates/UiLabels.xml
URL: 
http://svn.apache.org/viewvc/ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/resources/templates/UiLabels.xml?rev=1621696&r1=1621695&r2=1621696&view=diff
==============================================================================
--- 
ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/resources/templates/UiLabels.xml
 (original)
+++ 
ofbiz/branches/OFBIZ-5312-ofbiz-ecommerce-seo-2013-10-23/framework/resources/templates/UiLabels.xml
 Mon Sep  1 07:29:23 2014
@@ -3,17 +3,21 @@
     <property key="@component-resource-name@Application">
         <value xml:lang="en">@component-resource-name@ Application</value>
         <value xml:lang="zh">@component-resource-name@应用程序</value>
+        <value xml:lang="zh_TW">@component-resource-name@應用程序</value>
     </property>
     <property key="@component-resource-name@CompanyName">
         <value xml:lang="en">OFBiz: @component-resource-name@</value>
+        <value xml:lang="zh_TW">OFBiz: @component-resource-name@</value>
     </property>
     <property key="@component-resource-name@CompanySubtitle">
         <value xml:lang="en">Part of the Open For Business Family of Open 
Source Software</value>
         <value xml:lang="it">Un modulo della famiglia di software open source 
Open For Business</value>
         <value xml:lang="zh">开源软件OFBiz的组成部分</value>
+        <value xml:lang="zh_TW">開源軟體OFBiz的组成部分</value>
     </property>
     <property key="@component-resource-name@ViewPermissionError">
         <value xml:lang="en">You are not allowed to view this page.</value>
         <value xml:lang="zh">不允许你浏览这个页面。</value>
+        <value xml:lang="zh_TW">不允許您瀏覽這個頁面。</value>
     </property>
 </resource>
\ No newline at end of file


Reply via email to