问题
I have an unexpected behavior with available locales when native build. I have only one locale available in native mode.
My application is very simple :
@ApplicationPath("/api")
public class ApplicationPathConfiguration extends Application {
}
@Path("/locales")
public class LocaleController {
@GET
@Produces(MediaType.APPLICATION_JSON)
public Locale[] get() {
return Locale.getAvailableLocales();
}
}
After checkout if I launch the application in dev mode :
mvn quarkus:dev
You can call the endpoint : http://localhost:8080/api/locales
curl http://localhost:8080/api/locales
This endpoint return a lot of locales :
["","nn","ar_JO","bg","kea","nds","zu","am_ET","fr_DZ","ti_ET","bo_CN","hsb","qu_EC","ta_SG","lv","en_NU","zh_SG_#Hans","en_MS","en_GG","en_JM","vo","kkj","sr_ME","sv_SE","es_BO","dz_BT","mer","sah","en_ZM","fr_ML","br","ha_NG","ar_SA","fa_AF","dsb_DE","sk","os_GE","ml","en_MT","en_LR","ar_TD","en_GH","en_IL","sv","cs","el","tzm_MA","af","sw_UG","ses_ML","smn","tk_TM","sr_ME_#Cyrl","ar_EG","dsb","lkt_US","vai_LR_#Latn","ji_001","yo_NG","se_NO","khq","sw_CD","vo_001","en_PW","pl_PL","fil_PH","it_VA","sr_CS","ne_IN","es_PH","es_ES","es_CO","bg_BG","ji","ar_EH","bs_BA_#Latn","en_VC","nds_DE","nb_SJ","es_US","agq","hsb_DE","en_US_POSIX","en_150","ar_SD","en_KN","ha_NE","pt_MO","ebu","ro_RO","zh__#Hans","lb_LU","sr_ME_#Latn","es_GT","so_KE","dje_NE","bas_CM","fr_PM","ar_KM","fr_MG","no_NO_NY","es_CL","mn","agq_CM","kam_KE","teo","tr_TR","eu","fa_IR","en_MO","wo","shi__#Tfng","en_BZ","sq_AL","ar_MR","es_DO","ru","twq_NE","az","nmg_CM","fa","kl_GL","en_NR","nd","kk","az__#Cyrl","en_MP","en_GD","tk","hy","shi__#Latn","en_BW","en_AU","en_CY","kab_DZ","kde_TZ","ta_MY","ti_ER","nus_SS","en_RW","nd_ZW","sv_FI","ksb","luo","lb","ne","en_IE","zh_SG","ln_CD","en_KI","nnh_CM","om_ET","no","ja_JP","my","ka","ar_IL","mgh","or_IN","fr_MF","shi","kl","en_SZ","rwk_TZ","zh","es_PE","mgh_MZ","saq","az__#Latn","ta","en_GB","lag","zh_HK_#Hant","ar_SY","ksf_CM","bo","kk_KZ","es_PA","tt_RU","om_KE","ar_PS","en_AS","fr_VU","zh_TW","bez","kln","fr_MC","kw","pt_MZ","fr_NE","vai__#Latn","ksb_TZ","ksh","ur_IN","ln","en_JE","gsw_CH","ln_CF","en_CX","luy_KE","pt","en_AT","gl","kkj_CM","sr__#Cyrl","yue_CN_#Hans","es_GQ","kn_IN","ar_YE","to","en_SX","ga","qu","ru_KZ","en_TZ","et","en_PR","mua","ko_KP","in","ps","sn","nl_SR","rof","en_BS","km","zgh","fr_NC","be","gv","es","dua","gd_GB","jgo","nl_BQ","fr_CM","gsw","uz_UZ_#Cyrl","pa_IN_#Guru","en_KE","guz","mfe","asa_TZ","teo_UG","ja","fr_SN","or","brx","fr_MA","pt_LU","fr_BL","en_NL","mgo_CM","ln_CG","te","sl","ko_KR","el_CY","mr_IN","ha","es_MX","lrc_IR","gsw_FR","es_HN","hu_HU","ff_SN","sbp","sq_MK","sr_BA_#Cyrl","fi","uz","bs__#Cyrl","et_EE","sr__#Latn","en_SS","sw","bo_IN","fy_NL","ar_OM","tr_CY","nmg","rm","en_MG","fr_BI","uz_UZ_#Latn","bn","dua_CM","de_IT","lrc_IQ","vai__#Vaii","kn","fr_TN","sr_RS","de_CH","bn_BD","nnh","fr_PF","en_ZA","gu","pt_GQ","vun_TZ","jmc_TZ","en_TV","lo","fr_FR","en_PN","en_MH","fr_BJ","zh__#Hant","cu_RU","zh_HK_#Hans","nl_NL","sah_RU","en_GY","ps_AF","bs__#Latn","ky","mas","dyo_SN","os","bs_BA_#Cyrl","nl_CW","ar_DZ","sk_SK","pt_CH","fr_GQ","ff_CM","am","en_NG","fr_CI","ki_KE","en_PK","zh_CN","en_LC","rw","brx_IN","wo_SN","iw","gv_IM","mk_MK","en_TT","dav","sl_SI","fr_HT","te_IN","nl_SX","lrc","ses","ce","fr_CG","fr_BE","jgo_CM","mt_MT","es_VE","mg","mr","mer_KE","ko","nds_NL","en_BM","nb_NO","ak","seh","kde","dz","kea_CV","mgo","vi_VN","en_VU","en_US","to_TO","mfe_MU","seh_MZ","fr_BF","pa__#Guru","it_SM","fr_YT","gu_IN","ii_CN","pa_PK_#Arab","ast","fr_RE","fi_FI","yue__#Hans","ca_FR","sr_BA_#Latn","bn_IN","fr_GP","pa","zgh_MA","uk_UA","fr_DJ","rn","tg","rwk","hu","fr_CH","en_NF","twq","ha_GH","sr_XK_#Cyrl","bm","ar_SS","en_GU","nl_AW","de_BE","en_AI","en_CM","xog_UG","cs_CZ","tr","ca_ES","cgg","rm_CH","nyn_UG","ru_MD","ms_MY","ta_LK","ksf","en_TO","cy","en_PG","fr_CF","pt_TL","sq","fr","tg_TJ","en_ER","qu_PE","sr_BA","es_PY","de","es_EC","kok_IN","lg_UG","zu_ZA","fr_TG","sr_XK_#Latn","en_PH","ig_NG","fr_GN","prg_001","cgg_UG","zh_MO_#Hans","ksh_DE","lg","ru_RU","se_FI","ff","en_DM","en_CK","sd","ar_MA","ga_IE","en_BI","en_AG","fr_TD","en_WS","fr_LU","ebu_KE","bem_ZM","xog","ewo_CM","fr_CD","so","rn_BI","en_NA","ar_ER","kab","ms","nus","sn_ZW","prg","iw_IL","ug","es_EA","th_TH_TH_#u-nu-thai","hi","fr_SC","ca_IT","lag_TZ","en_SL","teo_KE","no_NO","ca_AD","zh_MO_#Hant","en_SH","vai","qu_BO","haw_US","vi","fr_CA","de_LU","sq_XK","dyo","en_KY","mt","it_CH","de_DE","si_LK","luo_KE","en_DK","yav","so_DJ","lt_LT","it_IT","eo","kam","ar_SO","en_ZW","ro","eo_001","ee","en_UM","nn_NO","fr_MU","pl","se_SE","en_TK","en_SI","mua_CM","ur","uz__#Arab","vai_LR_#Vaii","saq_KE","se","pt_GW","lo_LA","chr","ar_LB","af_ZA","ms_SG","ee_TG","ln_AO","be_BY","ff_GN","yue__#Hant","in_ID","es_BZ","ar_AE","hr_HR","luy","as","rof_TZ","it","pt_CV","ks_IN","uk","my_MM","ur_PK","mn_MN","da_DK","en_FM","es_PR","wae_CH","mzn","en_BE","ii","tt","fr_WF","ru_BY","mzn_IR","naq","fo_DK","en_SG","ee_GH","ar_BH","kln_KE","tzm","fur","om","hi_IN","en_CH","asa","yo_BJ","fo_FO","ast_ES","fr_KM","bez_TZ","fr_MQ","en_SD","es_AR","en_MY","ja_JP_JP_#u-ca-japanese","es_SV","pt_BR","ml_IN","sbp_TZ","fil","en_FK","uz__#Cyrl","is_IS","yue_HK_#Hant","hy_AM","en_GM","en_DG","fo","ne_NP","hr","pt_ST","ak_GH","lt","uz_AF_#Arab","fur_IT","ta_IN","ccp","en_SE","fr_GF","lkt","zh_CN_#Hans","is","es_419","si","pt_AO","en_001","en","guz_KE","gsw_LI","ccp_BD","es_IC","ca","ru_KG","fr_MR","ar_TN","ks","zh_TW_#Hant","bm_ML","kw_GB","ug_CN","as_IN","es_BR","zh_HK","khq_ML","sw_KE","en_SB","th_TH","rw_RW","chr_US","shi_MA_#Tfng","ar_IQ","nyn","yue","jmc","en_MW","naq_NA","mk","en_IO","ar_QA","en_DE","pa__#Arab","en_CC","bs","ro_MD","en_FI","pt_PT","fy","az_AZ_#Cyrl","th","dav_KE","ckb_IQ","shi_MA_#Latn","es_CU","ar","en_SC","en_VI","haw","eu_ES","en_UG","en_NZ","dje","es_UY","bas","mas_KE","ru_UA","sg_CF","el_GR","yav_CM","uz__#Latn","sg","da_GL","en_FJ","de_LI","en_BB","km_KH","smn_FI","hr_BA","de_AT","ckb_IR","nl","lu_CD","ca_ES_VALENCIA","ar_001","so_SO","lv_LV","ckb","es_CR","fr_GA","ar_KW","sr","ar_LY","sr_RS_#Cyrl","bem","en_MU","da","wae","gl_ES","en_IM","az_AZ_#Latn","en_LS","ig","en_HK","en_GI","ce_RU","en_CA","gd","ka_GE","fr_SY","sw_TZ","fr_RW","so_ET","nl_BE","ar_DJ","mg_MG","cy_GB","en_VG","cu","os_RU","sr_RS_#Latn","en_TC","ky_KG","sv_AX","af_NA","vun","en_IN","lu","ki","yo","es_NI","nb","ff_MR","sd_PK","mas_TZ","ti","kok","ewo","ms_BN","ccp_IN","br_FR"]
If I do the same in native mode :
mvn clean package -Pnative && ./target/QuarkusLocale-1.0-SNAPSHOT-runner
I obtain only one locale :
["fr_US"]
How can I obtain all available locale ?
Moreover, because of this behavior I have another problem. As you can see if I tried to develop a service who want use locale. Locale is not use and I can't apply currency format. To demonstrate that, I develop CurrencyController.
@Path("/currency")
public class CurrencyController {
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public String get(String localeString) {
String[] localeArray = localeString.split("-");
Locale locale = new Locale(localeArray[0], localeArray[1]);
NumberFormat numberFormat = NumberFormat.getCurrencyInstance(locale);
numberFormat.setCurrency(Currency.getInstance("USD"));
return numberFormat.format(1337);
}
}
You can call it with curl :
curl -X POST http://localhost:8080/api/currency -H "Content-Type: application/json" -d "en-GB"
> US$1,337.00
curl -X POST http://localhost:8080/api/currency -H "Content-Type: application/json" -d "en-US
> $1,337.00
If I do the same in native mode :
curl -X POST http://localhost:8080/api/currency -H "Content-Type: application/json" -d "en-GB"
> US$1,337.00
curl -X POST http://localhost:8080/api/currency -H "Content-Type: application/json" -d "en-US
> US$1,337.00
- Running Quarkus native-image plugin on GraalVM Version 19.3.1 CE
- Quarkus 1.4.1.Final
回答1:
This is a very well-known issue on GraalVM. Currently, the only way to bypass it is to create Feature that will scan all locales at run time:
- Add maven dependency
<dependency>
<groupId>org.graalvm.nativeimage</groupId>
<artifactId>svm</artifactId>
<version>19.3.1</version>
</dependency>
- Create Feature class
package org.example;
import com.oracle.svm.core.annotate.AutomaticFeature;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.hosted.Feature;
import org.graalvm.nativeimage.impl.RuntimeClassInitializationSupport;
import java.util.Locale;
@AutomaticFeature
public class NativeLanguageFeature implements Feature {
@Override
public void beforeAnalysis(BeforeAnalysisAccess access) {
ImageSingletons.lookup(RuntimeClassInitializationSupport.class).initializeAtBuildTime(SupportedLocales.class, "__");
final Locale[] availableLocales = Locale.getAvailableLocales();
final SupportedLocales supportedLocales = new SupportedLocales();
supportedLocales.locales = availableLocales;
ImageSingletons.add(SupportedLocales.class, supportedLocales);
}
public static class SupportedLocales {
public Locale[] locales;
}
}
- In a place where you want to access locales, check if SupportedLocales present in
ImageSingletons
@GET
@Produces(MediaType.APPLICATION_JSON)
public Locale[] get() {
if(ImageSingletons.contains(NativeLanguageFeature.SupportedLocales.class)){
final NativeLanguageFeature.SupportedLocales lookup = ImageSingletons.lookup(NativeLanguageFeature.SupportedLocales.class);
return lookup.locales;
}
return Locale.getAvailableLocales();
}
You probably will need to do same thing with money as well.
来源:https://stackoverflow.com/questions/61567216/graalvm-quarkus-locale-in-native-mode