Java - 針對i18n多國語言的簡易範例實作

針對之前的FileTransferService加入了i18n多國語言部分的處理,只要編輯.properties並且針對裡面的key值作相對應的value設置,在執行FileTranserService-1.0.jar時指定System Properties的語言TAG名,如此就可以載入對應的.properties
針對FileTransferService project新增Message.java及MessageUtils.java在message package下,並且在這一層另新增msg folder,將.properties統一放置此處!

首先說明Message.java

package message;

public class Message {
	public static String INPUT_PATH;
	public static String INPUT_PATH_NONEXIST;
	public static String HELP_MESSSAGE;
	
	static {
		MessageUtils.initializeMessages("message", Message.class);
	}
}
這支程式定義的變數名稱會與.properties的key相對應,也就是說在這邊新增一個變數,只要在.properties定義一樣名稱的key,那麼在其他程式只要呼叫Message.變數名,就可以取得.properties內key的設定value值,如此一來在呼叫方不用管load properties的行為,只要用就對了! 要注意的是首次呼叫Message.變數名時會呼叫MessageUtils.initializeMessages做初始化動作!

#message.properties內容
INPUT_PATH=Please specify path
INPUT_PATH_NONEXIST=The path non-exist
HELP_MESSSAGE=<<File Transfer Service>>\nStart client mode -> java ....

再來是MessageUtils.java

package message;

import java.lang.reflect.Field;
import java.util.Locale;
import java.util.Properties;

public class MessageUtils {
	private static String MSG_FILE_PATH = "msg/%s.properties";
	private static String LOCALE_TAG;
	static {
		LOCALE_TAG = System.getProperty("LOCALE_TAG");
		LOCALE_TAG = LOCALE_TAG == null ? "en" : LOCALE_TAG;
	}
	
	public static void initializeMessages(String baseName, Class<?> clazz) {
		Field fieldArray[] = clazz.getDeclaredFields();
		baseName = LOCALE_TAG.equals(Locale.ENGLISH.toLanguageTag()) ? baseName : baseName + "_" + LOCALE_TAG;

		Properties props = new Properties();
		try {
			props.load(Message.class.getResourceAsStream(String.format(MSG_FILE_PATH, baseName)));
		} catch (Exception e) {
			// TODO Auto-generated catch block
			if(props.isEmpty()) {
				System.out.println("Cannot found "+baseName+".properties");
			}
		}

		for(Field field : fieldArray) {		
			try {
				field.set(null, props.getOrDefault(field.getName(), ""));
			} catch (IllegalArgumentException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}
程式會先根據設定的LOCALE_TAG value來決定要載入哪個語言的.properties
因此要注意的是除了english以外,其他的.properties需要在檔名上設定好這個語言的TAG,如繁體中文是message_zh_TW.properties,tag名可以由Locale.TAIWAN.toLanguageTag()取得!

再來是initializeMessages function,它會載入Message.java指定的.properties,透過呼叫Message.class.getResourceAsStream這個function根據相對路徑找到.properties! 

最後就是呼叫Message.class.getDeclaredFields()取得Message.java宣告的static變數,再將每個變數set對應的.properties下的value值!

再來截一段Main.java使用的狀況

public class Main {
	public static final int PORT = 5880;
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		if(args.length == 0) {
			System.out.println(Message.HELP_MESSSAGE);
		}else {
			if(args.length == 1) {
				System.out.println(Message.INPUT_PATH);
			}else {
        .....

喔對了,程式寫好後當然要再次打包成jar檔,在此說明如何修改pom.xml,目的是將msg/*.properties一起打包進message folder,前一篇針對輸出FileTransferService-1.0.jar有詳細說明pom.xml,在此則在此基底下定義resources tag

<build>
		<sourceDirectory>${src.dir}</sourceDirectory>
		<resources>
			<resource>
				<directory>${src.dir}/message</directory>
				<includes>
					<include>msg/*</include>
				</includes>
				<targetPath>message</targetPath>
			</resource>
		</resources>
先根據哪個來源目錄,在此是src下的message,要copy進的檔案是什麼? 針對msg/*下所有的檔案,最後是target設在哪裡? 這邊是以target/classes這一層的相對位置,那就是在message目錄下啦!
PS. 如果不定義resources,那麼您將會看到message下只有包進編譯好的Message.class及MessageUtils.class

最後DEMO一下

留言