精华内容
下载资源
问答
  • JAVA实现国际化
    千次阅读
    2019-01-18 11:27:18

    1 Java国际化的思路

    Java程序的国际化的思路是将程序中的标签、提示等信息放在资源文件中,程序需要支持哪些国家、语言环境,就对应提供相应的资源文件。资源文件是key-value对,每个资源文件中的key是不变的,但value则随不同国家、语言改变

    2Java程序的国际化主要通过如下三个类完成

    Ø java.util.ResourceBundle:用于加载一个国家、语言资源包。

    Ø java.util.Locale:用于封装一个特定的国家/区域、语言环境。

    Ø java.text.MessageFormat:用于格式化带占位符的字符串。

    为了实现程序的国际化,必须先提供程序所需要的资源文件。资源文件的内容是很多key-value对。其中key是程序使用的部分,而value则是程序界面的显示字符串。

    资源文件的命名可以有如下三种形式:

    Ø baseName _ language _country.properties 

    Ø baseName _language.properties 

    Ø baseName.properties

    其中baseName是资源文件的基本名,用户可以自由定义。而language和country都不可随意变化,必须是Java所支持的语言和国家。

    3 Java支持的语言和国家

    事实上,Java不可能支持所有国家和语言,如需要获取Java所支持的语言和国家,可调用Locale类的getAvailableLocale方法获取,该方法返回一个Locale数组,该数组里包含了Java所支持的语言和国家

    package com.homecredit.smt.tipper;

    import java.util.Locale;
    import java.util.ResourceBundle;

    public class LocaleList {
        public static void main(String[] args) {
            // 返回Java所支持的全部国家和语言的数组
            Locale[] localeList = Locale.getAvailableLocales();
            // 遍历数组的每个元素,依次获取所支持的国家和语言
            for (int i = 0; i < localeList.length; i++) {
                // 打印出所支持的国家和语言
                System.out.println(localeList[i].getDisplayCountry() + "=" + localeList[i].getCountry() + " "
                        + localeList[i].getDisplayLanguage()
                        + "=" + localeList[i].getLanguage());
            }
            
            // 取得系统默认的国家/语言环境
            Locale myLocale = Locale.getDefault();
            // 根据指定国家/语言环境加载资源文件
            ResourceBundle bundle = ResourceBundle.getBundle("messages", myLocale);
            // 打印从资源文件中取得的消息
            System.out.println(bundle.getString("customer.name.label.default"));
        }
    }

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

    = =
    阿拉伯联合酋长国=AE 阿拉伯文=ar
    约旦=JO 阿拉伯文=ar
    叙利亚=SY 阿拉伯文=ar
    克罗地亚=HR 克罗地亚文=hr
    比利时=BE 法文=fr
    巴拿马=PA 西班牙文=es
    马耳他=MT 马耳他文=mt
    委内瑞拉=VE 西班牙文=es
    = 保加利亚文=bg
    台湾地区=TW 中文=zh
    = 意大利文=it
    = 朝鲜文=ko
    = 乌克兰文=uk
    = 拉托维亚文(列托)=lv
    丹麦=DK 丹麦文=da
    波多黎哥=PR 西班牙文=es
    越南=VN 越南文=vi
    美国=US 英文=en
    黑山=ME 塞尔维亚文=sr
    瑞典=SE 瑞典文=sv
    玻利维亚=BO 西班牙文=es
    新加坡=SG 英文=en
    巴林=BH 阿拉伯文=ar
    = 葡萄牙文=pt
    沙特阿拉伯=SA 阿拉伯文=ar
    = 斯洛伐克文=sk
    也门=YE 阿拉伯文=ar
    印度=IN 印地文=hi
    = 爱尔兰文=ga
    马耳他=MT 英文=en
    芬兰=FI 芬兰文=fi
    = 爱沙尼亚文=et
    = 瑞典文=sv
    = 捷克文=cs
    波斯尼亚和黑山共和国=BA 塞尔维亚文=sr
    = 希腊文=el
    乌克兰=UA 乌克兰文=uk
    = 匈牙利文=hu
    瑞士=CH 法文=fr
    = 印度尼西亚文=in
    阿根廷=AR 西班牙文=es
    埃及=EG 阿拉伯文=ar
    日本=JP 日文=ja
    萨尔瓦多=SV 西班牙文=es
    巴西=BR 葡萄牙文=pt
    = 白俄罗斯文=be
    冰岛=IS 冰岛文=is
    捷克共和国=CZ 捷克文=cs
    = 西班牙文=es
    波兰=PL 波兰文=pl
    = 土耳其文=tr
    西班牙=ES 加泰罗尼亚文=ca
    塞尔维亚及黑山=CS 塞尔维亚文=sr
    马来西亚=MY 马来文=ms
    = 克罗地亚文=hr
    = 立陶宛文=lt
    西班牙=ES 西班牙文=es
    哥伦比亚=CO 西班牙文=es
    保加利亚=BG 保加利亚文=bg
    = 阿尔巴尼亚文=sq
    = 法文=fr
    = 日文=ja
    波斯尼亚和黑山共和国=BA 塞尔维亚文=sr
    = 冰岛文=is
    巴拉圭=PY 西班牙文=es
    = 德文=de
    厄瓜多尔=EC 西班牙文=es
    美国=US 西班牙文=es
    苏丹=SD 阿拉伯文=ar
    = 英文=en
    罗马尼亚=RO 罗马尼亚文=ro
    菲律宾=PH 英文=en
    = 加泰罗尼亚文=ca
    突尼斯=TN 阿拉伯文=ar
    黑山=ME 塞尔维亚文=sr
    危地马拉=GT 西班牙文=es
    = 斯洛文尼亚文=sl
    韩国=KR 朝鲜文=ko
    塞浦路斯=CY 希腊文=el
    墨西哥=MX 西班牙文=es
    俄罗斯=RU 俄文=ru
    洪都拉斯=HN 西班牙文=es
    香港=HK 中文=zh
    挪威=NO 挪威文=no
    匈牙利=HU 匈牙利文=hu
    泰国=TH 泰文=th
    伊拉克=IQ 阿拉伯文=ar
    智利=CL 西班牙文=es
    = 芬兰文=fi
    摩洛哥=MA 阿拉伯文=ar
    爱尔兰=IE 爱尔兰文=ga
    = 马其顿文=mk
    土耳其=TR 土耳其文=tr
    爱沙尼亚=EE 爱沙尼亚文=et
    卡塔尔=QA 阿拉伯文=ar
    = 塞尔维亚文=sr
    葡萄牙=PT 葡萄牙文=pt
    卢森堡=LU 法文=fr
    阿曼=OM 阿拉伯文=ar
    = 泰文=th
    阿尔巴尼亚=AL 阿尔巴尼亚文=sq
    多米尼加共和国=DO 西班牙文=es
    古巴=CU 西班牙文=es
    = 阿拉伯文=ar
    = 俄文=ru
    新西兰=NZ 英文=en
    塞尔维亚=RS 塞尔维亚文=sr
    瑞士=CH 德文=de
    乌拉圭=UY 西班牙文=es
    = 马来文=ms
    希腊=GR 希腊文=el
    以色列=IL 希伯来文=iw
    南非=ZA 英文=en
    泰国=TH 泰文=th
    = 印地文=hi
    法国=FR 法文=fr
    奥地利=AT 德文=de
    = 荷兰文=nl
    挪威=NO 挪威文=no
    澳大利亚=AU 英文=en
    = 越南文=vi
    荷兰=NL 荷兰文=nl
    加拿大=CA 法文=fr
    拉脱维亚=LV 拉托维亚文(列托)=lv
    卢森堡=LU 德文=de
    哥斯达黎加=CR 西班牙文=es
    科威特=KW 阿拉伯文=ar
    = 塞尔维亚文=sr
    利比亚=LY 阿拉伯文=ar
    = 马耳他文=mt
    瑞士=CH 意大利文=it
    = 丹麦文=da
    德国=DE 德文=de
    阿尔及利亚=DZ 阿拉伯文=ar
    斯洛伐克=SK 斯洛伐克文=sk
    立陶宛=LT 立陶宛文=lt
    意大利=IT 意大利文=it
    爱尔兰=IE 英文=en
    新加坡=SG 中文=zh
    = 罗马尼亚文=ro
    加拿大=CA 英文=en
    比利时=BE 荷兰文=nl
    = 挪威文=no
    = 波兰文=pl
    中国=CN 中文=zh
    日本=JP 日文=ja
    希腊=GR 德文=de
    塞尔维亚=RS 塞尔维亚文=sr
    = 希伯来文=iw
    印度=IN 英文=en
    黎巴嫩=LB 阿拉伯文=ar
    尼加拉瓜=NI 西班牙文=es
    = 中文=zh
    马其顿王国=MK 马其顿文=mk
    白俄罗斯=BY 白俄罗斯文=be
    斯洛文尼亚=SI 斯洛文尼亚文=sl
    秘鲁=PE 西班牙文=es
    印度尼西亚=ID 印度尼西亚文=in
    英国=GB 英文=en
    hello

     

    4. 利用URLClassLoader 来读取指定文件路径下的配置  

     public static void main(String[] args) throws MalformedURLException {
            String messageFolder = "D:/MyWorkSpacePractice/TipService/tipper-service-conf/src/main/resources/conf/dev/tipper-service-conf/i18n";
            URI urI = new File(messageFolder).toURI();
            URL messageFileUrl = urI.toURL();

            ClassLoader classLoader = new URLClassLoader(new URL[] { messageFileUrl });
     
            ResourceBundle bundle = ResourceBundle.getBundle("messages", Locale.getDefault(), classLoader);

            // 打印从资源文件中取得的消息
            System.out.println(bundle.getString("customer.name.label.default"));
        }

     

     

    更多相关内容
  • 主要介绍了Java Spring项目国际化详细方法与实例,需要的朋友可以参考下
  • 用于Java国际化的工具
  • java国际化&时间处理

    2019-04-18 01:37:19
    NULL 博文链接:https://hoochiang.iteye.com/blog/1848534
  • java实现国际化

    2019-03-22 01:36:17
    NULL 博文链接:https://yuyu456.iteye.com/blog/903505
  • Java国际化配置

    2014-04-13 22:38:27
    Java写的一个国际化语言配置模块,可实现简单的国际化配置。
  • java 国际化转换

    2011-11-23 15:18:05
    java国际化操作,主要是讲字符转化为ASIIC
  • Java 后端国际化设计方案

    千次阅读 2021-01-27 15:47:31
    Java 后端国际化设计方案设计需求 设计需求 国际化配置集中到数据库中进行管理,包含前端部分国际化 最好可动态添加国际化的语种 好用易用 高效

    前言

    代码就不放全了,还在公司上跑着呢,就放一点非核心代码,工具类封装之类的

    设计需求

    • 国际化配置集中到数据库中进行管理,包含前端部分国际化
    • 最好可动态添加国际化的语种
    • 好用易用
    • 高效

    设计思路

    • 利用自定义注解来启用国际化,拦截所有返回请求进行处理
    • 大数据量处理使用多线程并行处理
    • 国际化数据保存在 Redis 中视为热点数据
    • 使用手动刷新方式,保证无缝刷新缓存
    • 国际化部分数据以 Json 形式来保存,保证扩展性
    • 语种以配置的形式保存,必要可添加语种
    • 需要多语言切换的数据全部以占位符代替,通过自定义注解统一替换
    • 当前语言环境通过前端带在请求头里给后端,后端默认为英文

    数据库设计

    • type: 类型,非空
    • module: 模块,可为空
    • label: 标签,非空
    • langs: 国际化 Json String,非空
    • to_web: 是否返回前端,不返回的就只是后端使用,将数据切成两半

    其中 type.module.label 的组合为唯一标识
    在这里插入图片描述

    需要国际化翻译的数据保存形式
    由于后端部分为自动生成的,因此 label 使用 UUID()
    在这里插入图片描述

    后端返回数据部分,比如:异常提示这部分的翻译
    在这里插入图片描述

    功能设计

    用到的工具类

    JsonUtils.java

    自定义注解

    个人认为只需要一个启动开关即可,没必要做成那种一个个接口去加

    /**
     * 开启国际化注解
     */
    @Import(TranslationAspect.class)
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface EnableTranslation {
    }
    

    使用
    在这里插入图片描述

    切面开发

    TranslationAspect

    /**
     * 国际化实现
     * @Author: linjinp
     * @Date: 2021/1/18 15:47
     */
    @Slf4j
    @Aspect
    public class TranslationAspect {
    
        // 默认语种
        private final static String DEFAULT_LANGUAGE = "en";
    
        // 切点
        // 指定拦截的包路径
        @Pointcut("execution(* com.xxx..*.*(..))")
        private void pointcut() {}
    
        @Around("pointcut()")
        private Object around(ProceedingJoinPoint pjp) throws Throwable {
    		// ...
    		// TODO
    		// ...
    		return ...
    	}
    

    从请求头获取当前语言环境

    在这里插入图片描述

    获取当前返回值的类型

    在这里插入图片描述

    /**
     * 获取列表中数据类型
     * @param obj
     * @return
     */
    private static Class getArrayListClass(Object obj) {
        // 判断空,判断类型是否为列表,判断是否有数据(无数据无法获取类型)
        if (obj != null && ArrayList.class.equals(obj.getClass()) && ((List) obj).size() > 0) {
            return ((List) obj).get(0).getClass();
        }
        return null;
    }
    
    /**
     * 获取数据类型
     * @param obj
     * @return
     */
    private static Class getClass(Object obj) {
        // 判断空,判断类型是否为列表,判断是否有数据(无数据无法获取类型)
        if (obj != null && !ArrayList.class.equals(obj.getClass())) {
            return obj.getClass();
        }
        return null;
    }
    

    将返回值转为 Json String 后,统一获取其中的占位符

    使用 StringBuilder 保存,防止大量的对象被创建
    在这里插入图片描述

    /**
    * 获取字符串中所有的变量参数
     * @param str 字符串/对象Json
     * @return
     */
    private static List<String> findParams(String str) {
        List<String> params = new ArrayList<>();
        // 转化为二进制
        char[] chars = str.toCharArray();
        // 找到标志的索引
        int findIndex = -1;
        for (int i = 0; i < chars.length; i ++) {
            // 判断 ${ 组合
            // i <= chars.length - 3 防越界,${A,假如以此结尾,$ 在 length - 3 位置
            if (i <= chars.length - 3 && chars[i] == '$' && chars[i + 1] == '{') {
                // 获取首个变量的下标索引
                findIndex = i + 2;
            }
            // 判断 } 且,已经存在索引下标,防止前面单独出现 } 的情况
            if (chars[i] == '}' && findIndex != -1) {
                // 添加变量
                params.add(new String(Arrays.copyOfRange(chars, findIndex, i)));
                // 重置标识
                findIndex = -1;
            }
        }
        return params;
    }
    

    替换返回值中所有的占位符为对应语言

    /**
     * 数据处理
     * @param lang 语言环境
     * @param data 返回数据
     * @param languages 语言包
     * @param params 需要替换的参数列表
     * @return
     */
    private static StringBuilder dataProcess(String lang, StringBuilder data, List<MultiLanguage> languages, List<String> params) {
        // 循环数据
        for (MultiLanguage language : languages) {
            // 有配置语言,非空对象,为后端使用的标签
            if (StringUtils.isNotBlank(language.getLangs()) && !"{}".equals(language.getLangs())) {
                for (String param : params) {
                    // 如果标签组合匹配
                    if (language.equalsCombination(param)) {
                        // 假如当前环境非默认语种,判断当前语种是否已经配置,如果没配置或为空,使用默认语种数据
                        if (!DEFAULT_LANGUAGE.equals(lang) && JsonUtils.toMap(language.getLangs()).containsKey(lang) && StringUtils.isNotBlank((String) JsonUtils.toMap(language.getLangs()).get(lang))) {
                            data.replace(0, data.length(), replaceRegex(data.toString(), param, StrUtil.nullToEmpty((String) JsonUtils.toMap(language.getLangs()).get(lang))));
                        } else {
                            data.replace(0, data.length(), replaceRegex(data.toString(), param, StrUtil.nullToEmpty((String) JsonUtils.toMap(language.getLangs()).get(DEFAULT_LANGUAGE))));
                        }
                    }
                }
            }
        }
        return data;
    }
    
    /**
     * 正则内容替换
     * @param source 数据
     * @param key 国际化标签
     * @param value 国际化对应值
     * @return
     */
    private static String replaceRegex(String source, String key, String value) {
        String regex = "\\$\\{"+key+"\\}";
        return source.replaceAll(regex, value);
    }
    

    最后要保证返回值的类型正确

    也是为了保证旧代码的兼容,比如你后来才加的国际化,这也是之前获取数据类型的原因
    在这里插入图片描述

    数据缓存

    构建线程池

    <!-- hutool -->
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.3.10</version>
    </dependency>
    
    /**
     * 线程池配置
     *
     * @Author: linjinp
     * @Date: 2020/9/29 10:54
     */
    @Slf4j
    @Component
    public class ExecutorConfig {
    
        public static ExecutorService executor;
    
        // 初始线程数量
        private final static int DEFAULT_NUM = 5;
    
        // 最大线程数
        private final static int MAX_NUM = 10;
    
        // 最大等待线程数
        private final static int MAX_WAITING = 100;
    
        @Bean
        public ExecutorService createExecutor() {
            this.executor = ExecutorBuilder.create()
                    // 默认初始化 5 个线程
                    .setCorePoolSize(DEFAULT_NUM)
                    // 最大线程数 10
                    .setMaxPoolSize(MAX_NUM)
                    // 最大等待线程数 100
                    .setWorkQueue(new LinkedBlockingQueue<>(MAX_WAITING))
                    .build();
            log.info("\n初始化线程池\n默认初始线程数:{}\n最大线程数:{}\n最大等待线程数:{}", DEFAULT_NUM, MAX_NUM, MAX_WAITING);
            return this.executor;
        }
    }
    

    数据缓存到 Redis

    刷新时将数据保存到 Redis 中

    这里使用 多线程 + 闭锁 的方式同步进行两方的处理,闭锁保证两个线程都完成后才继续执行,返回前端成功

    /**
     * 刷新国际化配置缓存
     * @return
     */
    @ApiOperation("刷新国际化配置缓存")
    @GetMapping(value = "/refresh")
    public ErrorMsg<Map<String, Object>> refresh() throws InterruptedException {
        // 利用闭锁保证两个线程都执行完毕后返回
        final CountDownLatch countDownLatch = new CountDownLatch(2);
    
        // 前端国际化数据,多线程处理
        ExecutorConfig.executor.execute(new Runnable(() -> {
            try {
            	// 构建前端所需的数据格式
                Map<String, Object> languageMap = buildLangToWeb();
                // 保存前端国际化部分数据 Map
                redisTemplate.opsForValue().set(RedisKeyConfig.LANGUAGE_ZONE, languageMap);
            } finally {
                countDownLatch.countDown();
            }
        }));
    
        // 后端国际化数据,多线程处理
        ExecutorConfig.executor.execute(new Runnable(() -> {
            try {
            	// 获取后端所需的数据列表
                List<MultiLanguage> languageList = buildLangToJava();
                // 保存后端国际化部分数据 List
                redisTemplate.opsForValue().set(RedisKeyConfig.LANGUAGE_JAVA, languageList);
            } finally {
                countDownLatch.countDown();
            }
        }));
        // 闭锁阻塞
        countDownLatch.await();
        return ErrorMsg.SUCCESS;
    }
    

    项目启动初始化国际化数据

    // 开启语言翻译
    @EnableTranslation
    public class AdminApplication {
        public static void main(String[] args) {
            SpringApplication.run(AdminApplication.class, args);
        }
    
        @Autowired
        private MultiLanguageController multiLanguageController;
    
        @Bean
        public CommandLineRunner runner() {
            return args -> {
                log.info("开始初始化国际化数据:{}", new Date());
                multiLanguageController.refresh();
                log.info("国际化初始化完成:{}", new Date());
            };
        }
    }
    

    效果展示

    中文返回
    在这里插入图片描述
    在这里插入图片描述
    英文返回
    在这里插入图片描述
    在这里插入图片描述

    展开全文
  • java国际化i18n

    2015-11-03 15:24:38
    java 国际化 i18n test
  • java国际化i8n

    2016-09-21 11:39:16
    本Demo使用配置文件实现java语言的国际化,没有过多的花哨,简易明了,稍微知道点java语言的同学都可以看得明白,有不明白的地方可随时留言,
  • Java中也有用于转换和划分地区的国际化java.lang.Locale,国际化在程序中设置语言和时间等时非常有用,下面我们就来详解Java中用于国际化的locale类
  • 多语言国际化资源文件生成工具,支持JAVA,DEPGLI等
  • java 实现国际化 中英文语言切换

    热门讨论 2013-11-29 11:25:42
    java实现国际化中英文语言切换 java语言切换JSP国际化 java实现国际化中英文语言切换 java语言切换JSP国际化
  • java国际化中文乱码问题解决包ResourceBundleEditor_v0.8.0.zip
  • JAVA国际化

    2012-12-15 12:29:56
    JAVA WEB国际化代码,能够使用英语和汉语,JAVA WEB国际化代码,能够使用英语和汉语,JAVA WEB国际化代码,能够使用英语和汉语,
  • java实现国际化I18N简单实例,没用任何框架.zip java实现国际化I18N简单实例,没用任何框架.zip
  • 最简单的java国际化例子最简单的java国际化例子最简单的java国际化例子
  • Java国际化及Spring国际化解决方法

    千次阅读 2015-11-03 15:08:55
    假设我们正在开发一个支持多国语言的Web应用程序,要求系统能够根据客户端的系统的语言类型返回对应的界面:英文的操作系统返回英文界面,而中文的操作系统则返回中文界面——这便是典型的i18n国际化问题。...

    假设我们正在开发一个支持多国语言的Web应用程序,要求系统能够根据客户端的系统的语言类型返回对应的界面:英文的操作系统返回英文界面,而中文的操作系统则返回中文界面——这便是典型的i18n国际化问题。
    国际化(internationalization)又称为 i18n(读法为i 18 n,据说是因为internationalization(国际化)这个单词从i到n之间有18个英文字母,i18n的名字由此而来)。
    对于有国际化要求的应用系统,我们不能简单地采用硬编码的方式编写用户界面信息、报错信息等内容,而必须为这些需要国际化的信息进行特殊处理。简单来说,就是为每种语言提供一套相应的资源文件,并以规范化命名的方式保存在特定的目录中,由系统自动根据客户端语言选择适合的资源文件。

    基础知识

    “国际化信息”也称为“本地化信息”,一般需要两个条件才可以确定一个特定类型的本地化信息,它们分别是“语言类型”和“国家/地区的类型”。如中文本地化信息既有中国大陆地区的中文,又有中国台湾、中国香港地区的中文,还有新加坡地区的中文。Java通过java.util.Locale类表示一个本地化对象,它允许通过语言参数和国家/地区参数创建一个确定的本地化对象。
    语言参数使用ISO标准语言代码表示,这些代码是由ISO-639标准定义的,每一种语言由两个小写字母表示。在许多网站上都可以找到这些代码的完整列表,下面的网址是提供了标准语言代码的信息:http://www.loc.gov/standards/iso639-2/php/English_list.php
    国家/地区参数也由标准的ISO国家/地区代码表示,这些代码是由ISO-3166标准定义的,每个国家/地区由两个大写字母表示。用户可以从以下网址查看ISO-3166的标准代码:http://www.iso.ch/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html
      表5-2给出了一些语言和国家/地区的标准代码:
    这里写图片描述

    Locale

    java.util.Locale是表示语言和国家/地区信息的本地化类,它是创建国际化应用的基础。下面给出几个创建本地化对象的示例:

    //①带有语言和国家/地区信息的本地化对象    
    Locale locale1 = new Locale("zh","CN");     
    
    //②只有语言信息的本地化对象    
    Locale locale2 = new Locale("zh");     
    
    //③等同于Locale("zh","CN")    
    Locale locale3 = Locale.CHINA;     
    
    //④等同于Locale("zh")    
    Locale locale4 = Locale.CHINESE;     
    
    //⑤获取本地系统默认的本地化对象    
    Locale locale5= Locale.getDefault(); 
    

    用户既可以同时指定语言和国家/地区参数定义一个本地化对象①,也可以仅通过语言参数定义一个泛本地化对象②。Locale类中通过静态常量定义了一些常用的本地化对象,③和④处就直接通过引用常量返回本地化对象。此外,用户还可以获取系统默认的本地化对象,如⑤所示。
      在测试时,如果希望改变系统默认的本地化设置,可以在启动JVM时通过命令参数指定:java -Duser.language=en -Duser.region=US MyTest。

    本地化工具类

    JDK的java.util包中提供了几个支持本地化的格式化操作工具类:NumberFormat、DateFormat、MessageFormat。下面,我们分别通过实例了解它们的用法:
      
    NumberFormat:

    Locale locale = new Locale("zh", "CN");    
    NumberFormat currFmt = NumberFormat.getCurrencyInstance(locale);    
    double amt = 123456.78;    
    System.out.println(currFmt.format(amt));  
    

      
    上面的实例通过NumberFormat按本地化的方式对货币金额进行格式化操作,运行实例,输出以下信息:

    ¥123,456.78 
    

    DateFormat:

    Locale locale = new Locale("en", "US");    
    Date date = new Date();    
    DateFormat df = DateFormat.getDateInstance(DateFormat.MEDIUM, locale);    
    System.out.println(df.format(date)); 
    

    通过DateFormat#getDateInstance(int style,Locale locale)方法按本地化的方式对日期进行格式化操作。该方法第一个入参为时间样式,第二个入参为本地化对象。运行以上代码,输出以下信息:

    Jan 8, 2007 
    

    MessageFormat在NumberFormat和DateFormat的基础上提供了强大的占位符字符串的格式化功能,它支持时间、货币、数字以及对象属性的格式化操作。下面的实例演示了一些常见的格式化功能:
      
    MessageFormat:

    //①信息格式化串    
    String pattern1 = "{0},你好!你于 {1} 在工商银行存入 {2} 元。";  
    String pattern2 = "At {1,time,short} On {1,date,long},{0} paid {2,number, currency}.";  
    
    //②用于动态替换占位符的参数    
    Object[] params = {"John", new GregorianCalendar().getTime(), 1.0E3};  
    
    //③使用默认本地化对象格式化信息    
    String msg1 = MessageFormat.format(pattern1, params);  
    
    //④使用指定的本地化对象格式化信息    
    MessageFormat mf = new MessageFormat(pattern2, Locale.US);  
    String msg2 = mf.format(params);  
    System.out.println(msg1);  
    System.out.println(msg2);  
    

    pattern1是简单形式的格式化信息串,通过{n}占位符指定动态参数的替换位置索引,{0}表示第一个参数,{1}表示第二个参数,以此类推。
    pattern2格式化信息串比较复杂一些,除参数位置索引外,还指定了参数的类型和样式。从pattern2中可以看出格式化信息串的语法是很灵活的,一个参数甚至可以出现在两个地方:如 {1,time,short}表示从第二个入参中获取时间部分的值,显示为短样式时间;而{1,date,long}表示从第二个入参中获取日期部分的值,显示为长样式时间。关于MessageFormat更详细的使用方法,请参见JDK的Javadoc。
    在②处,定义了用于替换格式化占位符的动态参数,这里,我们使用到了JDK5.0自动装包的语法,否则必须采用封装类表示基本类型的参数值。
    在③处,通过MessageFormat的format()方法格式化信息串。它使用了系统默认的本地化对象,由于我们是中文平台,因此默认为Locale.CHINA。而在④处,我们显式指定MessageFormat的本地化对象。
    运行上面的代码,输出以下信息:

    John,你好!你于 14-7-7 下午11:29 在工商银行存入 1,000 元。  
    At 11:29 PM On July 7, 2014,John paid $1,000.00.  
    

    如果应用系统中某些信息需要支持国际化功能,则必须为希望支持的不同本地化类型分别提供对应的资源文件,并以规范的方式进行命名。国际化资源文件的命名规范规定资源名称采用以下的方式进行命名:

      <资源名><语言代码><国家/地区代码>.properties
      
    其中,语言代码和国家/地区代码都是可选的。<资源名>.properties命名的国际化资源文件是默认的资源文件,即某个本地化类型在系统中找不到对应的资源文件,就采用这个默认的资源文件。
    <资源名>_<语言代码>.properties命名的国际化资源文件是某一语言默认的资源文件,即某个本地化类型在系统中找不到精确匹配的资源文件,将采用相应语言默认的资源文件。
    举一个例子:假设资源名为resource,则语言为英文,国家为美国,则与其对应的本地化资源文件命名为resource_en_US.properties。信息在资源文件以属性名/值的方式表示:

    greeting.common=How are you!  
    greeting.morning = Good morning!  
    greeting.afternoon = Good Afternoon! 
    

    对应语言为中文,国家/地区为中国大陆的本地化资源文件则命名为resource_zh_ CN.properties,资源文件内容如下:

    greeting.common=\u60a8\u597d\uff01  
    greeting.morning=\u65e9\u4e0a\u597d\uff01  
    greeting.afternoon=\u4e0b\u5348\u597d\uff01  
    

    本地化不同的同一资源文件,虽然属性值各不相同,但属性名却是相同的,这样应用程序就可以通过Locale对象和属性名精确调用到某个具体的属性值了。

    读者可能已经注意到,上面中文的本地化资源文件内容采用了特殊的编码表示中文字符,这是因为资源文件对文件内容有严格的要求:只能包含ASCII字符。所以必须将非ASCII字符的内容转换为Unicode代码的表示方式。如上面中文的resource_zh_CN.properties资源文件的三个属性值分别是“您好!”、“早上好!”和“下午好!”三个中文字符串对应的Unicode代码串。

    如果在应用开发时,直接采用Unicode代码编辑资源文件是很不方便的,所以,通常我们直接使用正常的方式编写资源文件,在测试或部署时再采用工具进行转换。JDK在bin目录下为我们提供了一个完成此项功能的native2ascii工具,它可以将中文字符的资源文件转换为Unicode代码格式的文件,命令格式如下:

      native2ascii [-reverse] [-encoding 编码] [输入文件 [输出文件]]
      
    resource_zh_CN.properties包含中文字符并且以UTF-8进行编码,假设将该资源文件放到d:\目录下,通过下面的命令就可以将其转换为Unicode代码的形式:

    D:\>native2ascii -encoding utf-8 d:\resource_zh_CN.properties  
    d:\resource_zh_CN_1.properties
    

    由于原资源文件采用UTF-8编码,所以必须显式通过-encoding指定编码格式。
    通过native2ascii命令手工转换资源文件,不但在操作上不方便,转换后资源文件中的属性内容由于采用了ASCII编码,阅读起来也不方便。很多IDE开发工具都有属性编辑器的插件,插件会自动将资源文件内容转换为ASCII形式的编码,同时以正常的方式阅读和编辑资源文件的内容,这给开发和维护带来了很大的便利。
    对于MyEclipse来说,使用MyEclipse Properties Editor编辑资源属性文件;
    对于Intellij IDEA来说,无须安装任何插件就自然支持资源属性文件的这种编辑方式了。
    如果应用程序中拥有大量的本地化资源文件,直接通过传统的File操作资源文件显然太过笨拙。Java为我们提供了用于加载本地化资源文件的方便类java.util.ResourceBoundle。

    ResourceBoundle为加载及访问资源文件提供便捷的操作,下面的语句从相对于类路径的目录中加载一个名为resource的本地化资源文件:

    ResourceBundle rb = ResourceBundle.getBundle("com/baobaotao/i18n/resource", locale)  
    

    通过以下的代码即可访问资源文件的属性值:

    rb.getString("greeting.common")
    

    来看下面的实例:

    ResourceBundle rb1 = ResourceBundle.getBundle("com/baobaotao/i18n/resource", Locale.US);  
    ResourceBundle rb2 = ResourceBundle.getBundle("com/baobaotao/i18n/resource", Locale.CHINA);  
    System.out.println("us:"+rb1.getString("greeting.common"));  
    System.out.println("cn:"+rb2.getString("greeting.common"));
    

    rb1加载了对应美国英语本地化的resource_en_US.properties资源文件;而rb2加载了对应中国大陆中文的resource_zh_CN.properties资源文件。运行上面的代码,将输出以下信息:

    us:How are you!  
    cn:你好!
    

    加载资源文件时,如果不指定本地化对象,将使用本地系统默认的本地化对象。所以,在中文系统中,ResourceBundle.getBundle(“com/baobaotao/i18n/resource”)语句也将返回和代码清单5-14中rb2相同的本地化资源。

    ResourceBundle在加载资源时,如果指定的本地化资源文件不存在,它按以下顺序尝试加载其他的资源:本地系统默认本地化对象对应的资源→默认的资源。
    上面的例子中,假设我们使用ResourceBundle.getBundle(“com/baobaotao/i18n/resource”,Locale.CANADA)加载资源,由于不存在resource_en_CA.properties资源文件,它将尝试加载resource_zh_CN.properties的资源文件,假设resource_zh_CN.properties资源文件也不存在,它将继续尝试加载resource.properties的资源文件,如果这些资源都不存在,将抛出java.util.MissingResourceException异常。

    在资源文件中使用格式化串

    在上面的资源文件中,属性值都是一般的字符串,它们不能结合运行时的动态参数构造出灵活的信息,而这种需求是很常见的。要解决这个问题很简单,只须使用带占位符的格式化串作为资源文件的属性值并结合使用MessageFormat就可以满足要求了。
    上面的例子中,我们仅向用户提供一般性问候,下面我们对资源文件进行改造,通过格式化串让问候语更具个性化:

    greeting.common=How are you!{0},today is {1}  
    greeting.morning = Good morning!{0},now is {1 time short}  
    greeting.afternoon = Good Afternoon!{0} now is {1 date long}  
    

    将该资源文件保存在fmt_resource_en_US.properties中,按照同样的方式编写对应的中文本地化资源文件fmt_resource_zh_CN.properties。
    下面,我们联合使用ResourceBoundle和MessageFormat得到美国英文的本地化问候语:

    //①加载本地化资源    
    ResourceBundle rb1 =     
                 ResourceBundle.getBundle("com/baobaotao/i18n/fmt_ resource",Locale.US);     
    ResourceBundle rb2 =     
                  ResourceBundle.getBundle("com/baobaotao/i18n/fmt_ resource",Locale.CHINA);    
    Object[] params = {"John", new GregorianCalendar().getTime()};    
    
    String str1 = new MessageFormat(rb1.getString("greeting.common"),Locale.US).format(params);    
    String str2 =new MessageFormat(rb2.getString("greeting.morning"),Locale.CHINA).format(params);    
    String str3 =new MessageFormat(rb2.getString("greeting.afternoon"),Locale.CHINA).format(params);    
    System.out.println(str1);    
    System.out.println(str2);    
    System.out.println(str3);
    

    运行以上的代码,将输出以下信息:

    How are you!John,today is 1/9/07 4:11 PM  
    早上好!John,现在是下午4:11  
    下午好!John,现在是2007年1月9日  
    

    MessageSource

    Spring定义了访问国际化信息的MessageSource接口,并提供了几个易用的实现类。首先来了解一下该接口的几个重要方法:
    String getMessage(String code, Object[] args, String defaultMessage, Locale locale) code表示国际化资源中的属性名;args用于传递格式化串占位符所用的运行期参数;当在资源找不到对应属性名时,返回defaultMessage参数所指定的默认信息;locale表示本地化对象;

    String getMessage(String code, Object[] args, Locale locale)  throws NoSuchMessageException
    

    与上面的方法类似,只不过在找不到资源中对应的属性名时,直接抛出NoSuchMessageException异常;

    String getMessage(MessageSourceResolvable resolvable, Locale locale)  throws NoSuchMessageException
    

    MessageSourceResolvable 将属性名、参数数组以及默认信息封装起来,它的功能和第一个接口方法相同。

    MessageSource的类结构

      MessageSource分别被HierarchicalMessageSource和ApplicationContext接口扩展,这里我们主要看一下HierarchicalMessageSource接口的几个实现类,如图5-7所示:
    这里写图片描述
    HierarchicalMessageSource接口添加了两个方法,建立父子层级的MessageSource结构,类似于前面我们所介绍的HierarchicalBeanFactory。该接口的setParentMessageSource (MessageSource parent)方法用于设置父MessageSource,而getParentMessageSource()方法用于返回父MessageSource。

    HierarchicalMessageSource接口最重要的两个实现类是ResourceBundleMessageSource和ReloadableResourceBundleMessageSource。它们基于Java的ResourceBundle基础类实现,允许仅通过资源名加载国际化资源。ReloadableResourceBundleMessageSource提供了定时刷新功能,允许在不重启系统的情况下,更新资源的信息。StaticMessageSource主要用于程序测试,它允许通过编程的方式提供国际化信息。而DelegatingMessageSource是为方便操作父MessageSource而提供的代理类。

    ResourceBundleMessageSource

    该实现类允许用户通过beanName指定一个资源名(包括类路径的全限定资源名),或通过beanNames指定一组资源名。在前面的代码清单中,我们通过JDK的基础类完成了本地化的操作,下面我们使用ResourceBundleMessageSource来完成相同的任务。读者可以比较两者的使用差别,并体会Spring所提供的国际化处理功能所带给我们的好处:
    通过ResourceBundleMessageSource配置资源

    <bean id="myResource"    
    class="org.springframework.context.support.ResourceBundleMessageSource">  
    <!--①通过基名指定资源,相对于类根路径-->  
    <property name="basenames">  
       <list>  
          <value>com/baobaotao/i18n/fmt_resource</value>  
       </list>  
    </property>  
    

    启动Spring容器,并通过MessageSource访问配置的国际化资源,如下代码清单所示:
    访问国际化消息:ResourceBundleMessageSource:

    String[] configs = {"com/baobaotao/i18n/beans.xml"};    
    ApplicationContext ctx = new ClassPathXmlApplicationContext(configs);    
    
    //①获取MessageSource的Bean    
    MessageSource ms = (MessageSource)ctx.getBean("myResource");     
    Object[] params = {"John", new GregorianCalendar().getTime()};    
    
    //②获取格式化的国际化信息    
    String str1 = ms.getMessage("greeting.common",params,Locale.US);    
    String str2 = ms.getMessage("greeting.morning",params,Locale.CHINA);    
    String str3 = ms.getMessage("greeting.afternoon",params,Locale.CHINA);    
    System.out.println(str1);    
    System.out.println(str2);    
    System.out.println(str3);
    

    比较代码清单中的代码,我们发现最主要的区别在于我们无须再分别加载不同语言、不同国家/地区的本地化资源文件,仅仅通过资源名就可以加载整套的国际化资源文件。此外,我们无须显式使用MessageFormat操作国际化信息,仅通过MessageSource# getMessage()方法就可以完成操作了。这段代码的运行结果与前面的代码的运行结果完全一样。

    ReloadableResourceBundleMessageSource

    前面,我们提到该实现类比之于ResourceBundleMessageSource的唯一区别在于它可以定时刷新资源文件,以便在应用程序不重启的情况下感知资源文件的变化。很多生产系统都需要长时间持续运行,系统重启会给运行带来很大的负面影响。这时,通过该实现类就可以解决国际化信息更新的问题。请看下面的配置:
    通过ReloadableResourceBundleMessageSource配置资源:

    <bean id="myResource"     
    lass="org.springframework.context.support.ReloadableResourceBundleMessageSource">    
    <property name="basenames">    
          <list>    
            <value>com/baobaotao/i18n/fmt_resource</value>    
          </list>    
       </property>    
       <!--① 刷新资源文件的周期,以秒为单位-->    
       <property name="cacheSeconds" value="5"/>     
     </bean>
    

    在上面的配置中,我们通过cacheSeconds属性让ReloadableResourceBundleMessageSource每5秒钟刷新一次资源文件(在真实的应用中,刷新周期不能太短,否则频繁的刷新将带来性能上的负面影响,一般不建议小于30分钟)。cacheSeconds默认值为-1表示永不刷新,此时,该实现类的功能就蜕化为ResourceBundleMessageSource的功能。

    我们编写一个测试类对上面配置的ReloadableResourceBundleMessageSource进行测试:

    String[] configs = {"com/baobaotao/i18n/beans.xml"};    
    ApplicationContext ctx = new ClassPathXmlApplicationContext(configs);    
    
    MessageSource ms = (MessageSource)ctx.getBean("myResource");    
    Object[] params = {"John", new GregorianCalendar().getTime()};    
    
    for (int i = 0; i < 2; i++) {    
        String str1 = ms.getMessage("greeting.common",params,Locale.US);        
        System.out.println(str1);    
        Thread.currentThread().sleep(20000); //①模拟程序应用,在此期间,我们更改资源文件     
    }   
    

      
    在①处,我们让程序睡眠20秒钟,在这期间,我们将fmt_resource_zh_CN.properties资源文件的greeting.common键值调整为:

    ---How are you!{0},today is {1}--- 
    

    我们将看到两次输出的格式化信息分别对应更改前后的内容,也即本地化资源文件的调整被自动生效了:

    How are you!John,today is 1/9/07 4:55 PM  
    ---How are you!John,today is 1/9/07 4:55 PM---  
    

    容器级的国际化信息资源

    在如图5-7所示的MessageSource类图结构中,我们发现ApplicationContext实现了MessageSource的接口。也就是说ApplicationContext的实现类本身也是一个MessageSource对象。
    将ApplicationContext和MessageSource整合起来,乍一看挺让人费解的,Spring这样设计的意图究竟是什么呢?原来Spring认为:在一般情况下,国际化信息资源应该是容器级。我们一般不会将MessageSource作为一个Bean注入到其他的Bean中,相反MessageSource作为容器的基础设施向容器中所有的Bean开放。只要我们考察一下国际化信息的实际消费场所就更能理解Spring这一设计的用意了。国际化信息一般在系统输出信息时使用,如Spring MVC的页面标签,控制器Controller等,不同的模块都可能通过这些组件访问国际化信息,因此Spring就将国际化消息作为容器的公共基础设施对所有组件开放。
    既然一般情况下我们不会直接通过引用MessageSource Bean使用国际信息,那如何声明容器级的国际化信息呢?我们其实在5.1.1节讲解Spring容器的内部工作机制时已经埋下了伏笔:在介绍容器启动过程时,我们通过代码清单5-1对Spring容器启动时的步骤进行剖析,④处的initMessageSource()方法所执行的工作就是初始化容器中的国际化信息资源:它根据反射机制从BeanDefinitionRegistry中找出名称为“messageSource”且类型为org.springframework.context.MessageSource的Bean,将这个Bean定义的信息资源加载为容器级的国际化信息资源。请看下面的配置:
    容器级资源的配置:

    <!--①注册资源Bean,其Bean名称只能为messageSource -->    
    <bean id="messageSource"     
              class="org.springframework.context.support.ResourceBundleMessageSource">    
      <property name="basenames">    
         <list>    
           <value>com/baobaotao/i18n/fmt_resource</value>    
         </list>    
      </property>    
    </bean> 
    

    下面,我们通过ApplicationContext直接访问国际化信息,如下代码清单所示:

    String[] configs = {"com/baobaotao/i18n/beans.xml"};    
    ApplicationContext ctx = new ClassPathXmlApplicationContext(configs);    
    //①直接通过容器访问国际化信息    
    Object[] params = {"John", new GregorianCalendar().getTime()};    
    
    String str1 = ctx.getMessage("greeting.common",params,Locale.US);    
    String str2 = ctx.getMessage("greeting.morning",params,Locale.CHINA);       
    System.out.println(str1);    
    System.out.println(str2);
    

    运行以上代码,输出以下信息:

    How are you!John,today is 1/9/07 5:24 PM  
    早上好!John,现在是下午5:24
    

    假设MessageSource Bean名字没有命名为“messageSource”,以上代码将抛出NoSuchMessageException异常。

    展开全文
  • Java Web国际化及乱码解决方案

    千次阅读 2019-09-27 08:39:24
    一、背景 项目过程中,难免需要提示中文或者英文提示信息,有了国际化,方便切换; 实际项目中,一般都不允许直接把中文提示信息写在代码中,避免其他国家程序猿看不懂(国际化...Java SDK自带国际化API:java.uti...

    一、背景

    1. 项目过程中,难免需要提示中文或者英文提示信息,有了国际化,方便切换;
    2. 实际项目中,一般都不允许直接把中文提示信息写在代码中,避免其他国家程序猿看不懂(国际化公司和开源项目涉及),也容易招来其他国家的恶意攻击。比如菊花公司把中文写在代码中就算成非常严重的违规。

    二、目标

    1. 在项目中引入简单易用的国际化框架,方便同事使用。

    三、步骤

    1. Java SDK自带国际化API:java.util.ResourceBundle,使用也特别简单,目前就选定使用它;
    2. 对应的国际化资源目录结构如下:
      国际化截图
      说明下:我这里是采用maven作为代码构建工具,资源默认路径是在src/main/resources,编译后对应路径为$bin/classes目录。$bin表示代码生成class的根路径。$bin在maven中实际默认为:与src同级的target目录。
    3. 了解了下ResourceBundle的用法,编写测试类:
    import org.junit.Test;
    
    import java.io.UnsupportedEncodingException;
    import java.util.Locale;
    import java.util.ResourceBundle;
    
    import static org.junit.Assert.assertTrue;
    
    public class I18nTest
    {
        @Test
        public void testI18n() throws UnsupportedEncodingException
        {
            Locale locale = Locale.getDefault();
            System.out.println("current locale:" + locale.toString());
            ResourceBundle bundle = ResourceBundle.getBundle("i18n/tips", locale);
            String msg = bundle.getString("test.failed");
            System.out.println("current msg:" + msg);
            assertTrue(null != msg);
        }
    }
    

    其中ResourceBundle.getBundle的第一个参数叫baseName,就是相对于classes目录的文件路径,因为涉及国际化,所以不能带上国家(区域)后缀,也不需要带上porperties这个文件类型后缀。记住baseName是相对路径,不要在i18n/tips前面加上‘/’。
    4. 国家(区域)后缀有标准的定义。也有网友总结的参照表
    5. 执行上述测试代码过程中,发现输入中文时,会出现乱码。原因:IDE工具默认的字符编码不是UTF-8。建议把IDE的所有文件类型编码都设置成UTF-8,同时设置properties内容转换为ASCII码。我使用的是IDEA,设置如下:
    properties编码设置
    6. 设置后,会发现tips_zh_CN.properties中文展示如下:
    国际化展示
    7. 再次执行,乱码消失了。
    8. 验证通过后,封装对应的工具类:

    public final class I18nUtils
    {
        /**
         * 获取国际化值
         *
         * @param key
         * @return
         */
        public static String get(String key)
        {
            String value = BUNDLE.getString(key);
            if (StringUtils.isEmpty(value))
            {
                value = StringUtils.EMPTY;
            }
            LOGGER.info("i18n:[{}]={}", key, value);
            return value;
        }
    
        private static final Logger LOGGER = LogManager.getLogger(I18nUtils.class);
    
        //国际化文件路径
        private static final String I18N_FILE = "i18n/tips";
    
        //国际化bundle
        private static ResourceBundle BUNDLE;
    
        static
        {
            BUNDLE = ResourceBundle.getBundle(I18N_FILE, Locale.getDefault());
        }
    
        private I18nUtils()
        {
        }
    } 
    
    1. 在springboot框架下使用该工具类给页面返回中文结果时,发现还是存在乱码。springboot的application.properties编码配置如下:
    server.tomcat.uri-encoding=UTF-8
    spring.banner.charset=UTF-8
    spring.messages.encoding=UTF-8
    spring.http.encoding.charset=UTF-8
    spring.http.encoding.force=true
    spring.http.encoding.enabled=true
    

    给前台返回结果的代码(过滤器)如下:

               Writer writer = response.getWriter();
               String failedMsg = I18nUtils.get(“test.failed”);
               String failedJson = JsonUtils.toJson(failedMsg);
               writer.write(failedJson);
               writer.flush();
    
    1. 刚开始考虑还有没有什么编码设置给遗漏了,包括是不是springboot集成的springmvc部件没有指定编码,结果发现根本不起作用。后面只能怀疑是response响应数据时,没有指定编码。
    2. 在上述代码前加上ContentType设置(“application/json;utf-8”):
                response.setContentType(ContentType.APPLICATION_JSON.toString());
    

    问题解决。

    四、总结

    1. 在这么多年的Java Web项目中,碰到了很多次乱码的问题,而且每次问题都还不一样,解决方案也不一样T_T;
    2. Java自带的国际化工具比想象的好用;
    3. 网上关于Java自带国际化的使用大多只说了部分问题和部分代码,基本上没有看到像我这样完整写出来的,希望对你有帮助;
    展开全文
  • Java 国际化问题

    2009-08-21 08:49:02
    Java 国际化 Java 国际化 Java 国际化
  • java实现i18n国际化

    千次阅读 2021-01-29 17:40:33
    internationalization (国际化)简称 i18n,因为在i和n之间还有18个字符,localization(本地化 ),简称L10n。 一般用语言_地区的形式表示一种语言,如 zh_CN, zh_TW. 常见的有: zh_cn: 简体中文 zh_hk: 繁体中文...
  • 跟我学Java入门到精通培训教程第5章Java国际化技术及应用实例 教学目标 在本讲中希望您能掌握和了解如下知识点Java中如何解决国际化的问题实现国际化程序的基本思路Java中与国际化相关的包与类利用資源字串实现国际...
  • 深入理解Java国际化

    万次阅读 多人点赞 2014-07-09 23:26:25
    假设我们正在开发一个支持多国语言的Web应用程序,要求系统能够根据客户端的系统的语言类型返回对应的界面:英文的操作系统返回英文界面,而中文的操作系统则返回中文界面——这便是典型的i18n国际化问题。...
  • Java国际化处理

    千次阅读 2018-11-22 23:14:38
    最近在做Kettle8.1的国际化工作,闲暇之余,就看了看Java国际化处理,明白程序怎么样找到对应的国际化文件。 说到国际化,经常看到一个东西叫i18n,其实是internationalization的缩写(ps:以后起昵称什么的就...
  • java国际化之时区问题处理

    千次阅读 2019-05-08 10:27:59
    国际化的项目中需要处理的日期时间问题主要有两点: 1、日期时间的国际化格式问题处理; 2、日期时间的时区问题处理,这两个问题要区分开,不要弄混了。 日期时间国际化化格式处理 对应的关键词:Locale ...
  • Java 中的国际化

    万次阅读 2018-08-06 17:46:45
    国际化 ,英文叫 internationalization 单词太长 ,又被简称为 i18n(取头取尾中间有18个字母)不经大声呼喊 ,这都行 !接着看什么是国际化国际化是指让产品或是程序在无需做出...在 Java 中实现国际化主要是借...
  • Java web 国际化

    热门讨论 2010-11-03 18:37:26
    Java web 国际化
  • java国际化自定义MessageSource

    千次阅读 2018-03-20 10:35:52
    spring boot国际化的东西就不多说,百度粘贴复制的的东西一大堆,不知道谁是原创,这里贴出我能找到的最早的博文链接供大家学习: 58. Spring Boot国际化(i18n)【从零开始学Spring Boot】本文仅是自定义...
  • Java国际化(i18n)字符串与Unicode转换

    千次阅读 2021-03-18 10:25:18
    java中,文本是以Unicode格式内部存储的。 如果输入/输出格式不同,则需要转换。转换以下示例将展示将Unicode字符串转换为UTF8字节,以及将UTF8字节转为Unicode字节转换。文件:IOTester.java -import java.io....
  • Java 国际化操作

    2010-09-10 11:44:59
    Java 国际化操作Java 国际化操作Java 国际化操作Java 国际化操作Java 国际化操作Java 国际化操作Java 国际化操作

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 140,590
精华内容 56,236
关键字:

java国际化作用

java 订阅