解決Spring國際化文案占位符失效問題的方法
寫在前面:接下來很長一段時間的文章主要會記錄一些項目中實際遇到的問題及對應(yīng)的解決方案,在相應(yīng)代碼分析時會直指問題所在,不會將無關(guān)的流程代碼貼出,感興趣的讀者可以自行跟蹤。同時希望大家能夠?qū)⑿牡皿w會在評論區(qū)分享出來,讓大家共同進步!
環(huán)境或版本:Spring 3.2.3
現(xiàn)象:利用Spring自帶的MessageSource來處理國際化文案,us狀態(tài)下的文案有部分占位符未被替換,cn狀態(tài)下的正常。文案如下:
tms.pallet.order.box.qty=The total palletized boxes quantity {0} doesn't match with the received boxes quantity {1},Please double check!
tms.pallet.order.box.qty=打板總箱數(shù)件{0},與訂單收貨總箱數(shù){1}不一致。請檢查!
直覺:是不是英文文案太長了,Spring處理時對長度做了限制,仔細想了想Spring應(yīng)該不會設(shè)計的這么坑。
排查:斷點跟蹤Spring源碼(入口:MessageSource的getMessage方法),最后發(fā)現(xiàn)了MessageFormat中這樣的一段處理方法:
// Indices for segments private static final int SEG_RAW = 0; private static final int SEG_INDEX = 1; private static final int SEG_TYPE = 2; private static final int SEG_MODIFIER = 3; // modifier or subformat /** * Sets the pattern used by this message format. * The method parses the pattern and creates a list of subformats * for the format elements contained in it. * Patterns and their interpretation are specified in the * <a href="#patterns" rel="external nofollow" >class description</a>. * * @param pattern the pattern for this message format * @exception IllegalArgumentException if the pattern is invalid */ @SuppressWarnings("fallthrough") // fallthrough in switch is expected, suppress it public void applyPattern(String pattern) { StringBuilder[] segments = new StringBuilder[4]; // Allocate only segments[SEG_RAW] here. The rest are // allocated on demand. segments[SEG_RAW] = new StringBuilder(); int part = SEG_RAW; int formatNumber = 0; boolean inQuote = false; int braceStack = 0; maxOffset = -1; for (int i = 0; i < pattern.length(); ++i) { char ch = pattern.charAt(i); if (part == SEG_RAW) { if (ch == '\'') { if (i + 1 < pattern.length() && pattern.charAt(i+1) == '\'') { segments[part].append(ch); // handle doubles ++i; } else { inQuote = !inQuote; } } else if (ch == '{' && !inQuote) { part = SEG_INDEX; if (segments[SEG_INDEX] == null) { segments[SEG_INDEX] = new StringBuilder(); } } else { segments[part].append(ch); } } else { if (inQuote) { // just copy quotes in parts segments[part].append(ch); if (ch == '\'') { inQuote = false; } } else { switch (ch) { case ',': if (part < SEG_MODIFIER) { if (segments[++part] == null) { segments[part] = new StringBuilder(); } } else { segments[part].append(ch); } break; case '{': ++braceStack; segments[part].append(ch); break; case '}': if (braceStack == 0) { part = SEG_RAW; makeFormat(i, formatNumber, segments); formatNumber++; // throw away other segments segments[SEG_INDEX] = null; segments[SEG_TYPE] = null; segments[SEG_MODIFIER] = null; } else { --braceStack; segments[part].append(ch); } break; case ' ': // Skip any leading space chars for SEG_TYPE. if (part != SEG_TYPE || segments[SEG_TYPE].length() > 0) { segments[part].append(ch); } break; case '\'': inQuote = true; // fall through, so we keep quotes in other parts default: segments[part].append(ch); break; } } } } if (braceStack == 0 && part != 0) { maxOffset = -1; throw new IllegalArgumentException("Unmatched braces in the pattern."); } this.pattern = segments[0].toString(); }
上面的這段代碼寫的有點讓人費解,略微奇特,我們主要看第一個邏輯分支:對每一個待處理的國際化文案模板串中的字符進行遍歷,當(dāng)字符為"'"時,判斷后一個字符是否也為“'”,如果是則將“‘”拼接到已處理的StringBuilder中,不是則將inQuote至為True,如果該字符不會‘{'且inQuote為false則將part重新置為0,并且segments[SEG_INDEX]=null的話重新創(chuàng)建StringBuilder對象,否則繼續(xù)拼接。
原因分析:
- 結(jié)合我們配置的英文文案(其中一共有兩個占位符,在這這兩占位符之前有一個單引號),根據(jù)上面Spring的處理源碼看,實際處理會是:對該字符串進行逐個字符處理,逐個拼接到已處理的StringBuilder中,當(dāng)處理到‘{'時,此處part將被置為1,同時segments第1個存儲位上會引用StringBuilder類型的對象,程序繼續(xù)處理下面的待處理的字符,繼續(xù)拼接(請自行看part!= SEG_RAW的邏輯分支),直到處理到‘}'時,part被重新賦值為0,sefgments的其他位被清空,于是繼續(xù)處理下面的字符串繼續(xù)拼接,處理到單引號時,inQuote被置為True,接下來就一路拼接了,不再對后面的“{“做占位符處理。
- 中文文案中兩個占位符之間并沒有出現(xiàn)單引號,因此解決了問題現(xiàn)象中的第二點,中文文案顯示正常。
解決方案:
從源碼看只有一種解決方式,{}之間的單引號需要成對出現(xiàn),我們的處理方式是將文案修改為了:
tms.pallet.order.box.qty=The total palletized boxes quantity {0} doesn''t match with the received boxes quantity {1},Please double check!
直接修改文案其實并不是一種很好的解決方法,最好是能夠重寫Spring調(diào)用applyPattern方法前的某一方法來將單引號替換為雙引號。無奈spring 3.2.3版本中對應(yīng)國際化的處理方法一路private,不給你重寫的機會。
查閱相關(guān)資料得知,在Spring4.3.2版本中可以通過重寫ResourceBundleMessageSource類中的getStringOrNull方法來實現(xiàn)。
長遠方案:升級項目中的Spring版本,同時使用更多的新版特性。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
springboot+thymeleaf整合阿里云OOS對象存儲圖片的實現(xiàn)
本文主要介紹了springboot+thymeleaf整合阿里云OOS對象存儲圖片的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05Java數(shù)據(jù)結(jié)構(gòu)之圖的領(lǐng)接矩陣詳解
圖的領(lǐng)接矩陣存儲方式是用兩個數(shù)組來表示圖。一個一位數(shù)組存儲圖中頂點信息,一個二維數(shù)組存儲圖中的邊或弧的信息。本文將為大家重點介紹一下數(shù)據(jù)結(jié)構(gòu)中的圖的鄰接矩陣,快來跟隨小編一起學(xué)習(xí)吧2021-11-11詳解如何在Spring中為@Value注解設(shè)置默認值
在Spring開發(fā)中,我們經(jīng)常會遇到需要從配置文件中讀取屬性的情況,@Value注解是Spring提供的一種便捷方式,能夠讓我們輕松地將配置文件中的屬性注入到Spring Bean中,2024-10-10反射機制:getDeclaredField和getField的區(qū)別說明
這篇文章主要介紹了反射機制:getDeclaredField和getField的區(qū)別說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06