Initial project
This commit is contained in:
@ -0,0 +1,31 @@
|
||||
package com.imyeyu.lang.mapper;
|
||||
|
||||
import com.imyeyu.java.bean.Language;
|
||||
import com.imyeyu.java.bean.LanguageMapping;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
|
||||
/**
|
||||
* @author 夜雨
|
||||
* @version 2024-04-01 16:25
|
||||
*/
|
||||
public abstract class AbstractLanguageMapper implements LanguageMapping {
|
||||
|
||||
protected static final MessageFormat FORMAT = new MessageFormat("");
|
||||
|
||||
protected final Language language;
|
||||
|
||||
protected boolean isDebugging = false;
|
||||
|
||||
public AbstractLanguageMapper(Language language) {
|
||||
this.language = language;
|
||||
}
|
||||
|
||||
public Language getLanguage() {
|
||||
return language;
|
||||
}
|
||||
|
||||
public void setDebugging(boolean debugging) {
|
||||
isDebugging = debugging;
|
||||
}
|
||||
}
|
||||
35
src/main/java/com/imyeyu/lang/mapper/FileLanguageMap.java
Normal file
35
src/main/java/com/imyeyu/lang/mapper/FileLanguageMap.java
Normal file
@ -0,0 +1,35 @@
|
||||
package com.imyeyu.lang.mapper;
|
||||
|
||||
import com.imyeyu.io.IO;
|
||||
import com.imyeyu.java.bean.Language;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* @author 夜雨
|
||||
* @version 2024-04-09 01:00
|
||||
*/
|
||||
public class FileLanguageMap extends PropertiesLanguageMap {
|
||||
|
||||
/**
|
||||
* 默认构造
|
||||
*
|
||||
* @param language 所属语言
|
||||
*/
|
||||
public FileLanguageMap(Language language) {
|
||||
super(language);
|
||||
}
|
||||
|
||||
public void load(String path) {
|
||||
try {
|
||||
InputStream is = IO.getInputStream(path.formatted(language.toString()));
|
||||
Properties properties = new Properties();
|
||||
properties.load(new InputStreamReader(is));
|
||||
load(properties);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("load language properties file error", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
124
src/main/java/com/imyeyu/lang/mapper/LanguageMap.java
Normal file
124
src/main/java/com/imyeyu/lang/mapper/LanguageMap.java
Normal file
@ -0,0 +1,124 @@
|
||||
package com.imyeyu.lang.mapper;
|
||||
|
||||
import com.imyeyu.java.TimiJava;
|
||||
import com.imyeyu.java.bean.Language;
|
||||
import com.imyeyu.utils.Text;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author 夜雨
|
||||
* @version 2024-04-03 10:11
|
||||
*/
|
||||
public class LanguageMap extends AbstractLanguageMapper {
|
||||
|
||||
protected final Map<String, String> map;
|
||||
|
||||
/**
|
||||
* 默认构造
|
||||
*
|
||||
* @param language 所属语言
|
||||
*/
|
||||
public LanguageMap(Language language) {
|
||||
super(language);
|
||||
this.map = new HashMap<>();
|
||||
}
|
||||
|
||||
public void union(LanguageMap map) {
|
||||
this.map.putAll(map.map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(String key, String value) {
|
||||
map.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean has(String key) {
|
||||
return map.containsKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文本,支持二次映射,没有找到映射时返回入参键
|
||||
* <pre>
|
||||
* test.msg=Hello world
|
||||
* mapping=@test.msg
|
||||
* no_mapping=\@test.msg
|
||||
*
|
||||
* Multilingual.text("mapping"); // Hello world
|
||||
* Multilingual.text("no_mapping"); // @test.msg
|
||||
* </pre>
|
||||
*
|
||||
* @param key 键
|
||||
* @return 文本值
|
||||
*/
|
||||
@Override
|
||||
public String text(String key) {
|
||||
if (map.containsKey(key)) {
|
||||
String result = map.get(key);
|
||||
if (result.startsWith("@")) {
|
||||
// 递归映射
|
||||
return text(result.substring(1));
|
||||
} else {
|
||||
if (result.startsWith("\\@")) {
|
||||
return result.substring(1);
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (TimiJava.isNotEmpty(key)) {
|
||||
// 推测
|
||||
String guessKey = key;
|
||||
LinkedHashMap<String, Number> keySRL = Text.similarityRatioList(map.keySet(), key);
|
||||
if (keySRL.keySet().iterator().hasNext()) {
|
||||
String k = keySRL.keySet().iterator().next();
|
||||
Number ratio = keySRL.get(k);
|
||||
if (.7 < ratio.doubleValue()) {
|
||||
guessKey = k;
|
||||
}
|
||||
}
|
||||
System.err.printf("not found language mapping for key: [%s], it is [%s]?%n", key, guessKey);
|
||||
if (isDebugging) {
|
||||
throw new RuntimeException("not found language mapping key: " + key);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 找不到映射,直接返回键
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文本
|
||||
*
|
||||
* @param key 键
|
||||
* @param def 默认值(没有找到映射值时)
|
||||
* @return 获取结果
|
||||
*/
|
||||
@Override
|
||||
public String text(String key, String def) {
|
||||
String result = text(key);
|
||||
return key.equals(result) ? def : result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入参数获取文本
|
||||
*
|
||||
* @param key 键
|
||||
* @param args 参数
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public String textArgs(String key, Object... args) {
|
||||
String result = text(key);
|
||||
if (map.containsKey(result)) {
|
||||
// 没有映射值
|
||||
return result + Arrays.toString(args);
|
||||
}
|
||||
FORMAT.applyPattern(result);
|
||||
return FORMAT.format(args);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
package com.imyeyu.lang.mapper;
|
||||
|
||||
import com.imyeyu.java.bean.Language;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* @author 夜雨
|
||||
* @version 2024-04-09 00:50
|
||||
*/
|
||||
public class PropertiesLanguageMap extends LanguageMap {
|
||||
|
||||
/**
|
||||
* 默认构造
|
||||
*
|
||||
* @param language 所属语言
|
||||
*/
|
||||
public PropertiesLanguageMap(Language language) {
|
||||
super(language);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public void load(Properties properties) {
|
||||
map.putAll(new HashMap(properties));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
package com.imyeyu.lang.mapper;
|
||||
|
||||
import com.imyeyu.io.IO;
|
||||
import com.imyeyu.java.bean.Language;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* @author 夜雨
|
||||
* @version 2024-04-09 00:55
|
||||
*/
|
||||
public class ResourcesLanguageMap extends PropertiesLanguageMap {
|
||||
/**
|
||||
* 默认构造
|
||||
*
|
||||
* @param language 所属语言
|
||||
*/
|
||||
public ResourcesLanguageMap(Language language) {
|
||||
super(language);
|
||||
}
|
||||
|
||||
public void load(String path) {
|
||||
try {
|
||||
InputStream is = IO.resourceToInputStream(getClass(), path.formatted(language.toString()));
|
||||
if (is != null) {
|
||||
Properties properties = new Properties();
|
||||
properties.load(new InputStreamReader(is));
|
||||
load(properties);
|
||||
} else {
|
||||
throw new FileNotFoundException("not found language properties file: " + path.formatted(language.toString()));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("load language properties file error", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
29
src/main/java/com/imyeyu/lang/multi/FileMultilingual.java
Normal file
29
src/main/java/com/imyeyu/lang/multi/FileMultilingual.java
Normal file
@ -0,0 +1,29 @@
|
||||
package com.imyeyu.lang.multi;
|
||||
|
||||
import com.imyeyu.java.bean.Language;
|
||||
import com.imyeyu.lang.mapper.AbstractLanguageMapper;
|
||||
import com.imyeyu.lang.mapper.FileLanguageMap;
|
||||
import com.imyeyu.lang.mapper.LanguageMap;
|
||||
|
||||
/**
|
||||
* @author 夜雨
|
||||
* @version 2024-04-09 01:06
|
||||
*/
|
||||
public class FileMultilingual extends Multilingual {
|
||||
|
||||
public void addAll(String path) {
|
||||
Language[] values = Language.values();
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
FileLanguageMap mapper = new FileLanguageMap(values[i]);
|
||||
mapper.load(path);
|
||||
if (multilingualMap.containsKey(values[i])) {
|
||||
AbstractLanguageMapper existMapper = multilingualMap.get(values[i]);
|
||||
if (existMapper instanceof LanguageMap map) {
|
||||
map.union(mapper);
|
||||
}
|
||||
} else {
|
||||
add(values[i], mapper);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
218
src/main/java/com/imyeyu/lang/multi/Multilingual.java
Normal file
218
src/main/java/com/imyeyu/lang/multi/Multilingual.java
Normal file
@ -0,0 +1,218 @@
|
||||
package com.imyeyu.lang.multi;
|
||||
|
||||
import com.imyeyu.java.bean.CallbackArg;
|
||||
import com.imyeyu.java.bean.Language;
|
||||
import com.imyeyu.java.bean.LanguageMapping;
|
||||
import com.imyeyu.java.ref.Ref;
|
||||
import com.imyeyu.lang.mapper.AbstractLanguageMapper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 多语言系统,支持多文件多语言加载
|
||||
* <pre>
|
||||
* // lang 文件
|
||||
* say.msg=hello {0}
|
||||
*
|
||||
* // 加载
|
||||
* Multilingual lang = Multilingual.getInstance();
|
||||
* lang.load(Language.zh_CN, "lang/timijava/%s.lang");
|
||||
*
|
||||
* // 加载所有支持的映射文件
|
||||
* lang.loadAll("lang/timifx/%s.lang");
|
||||
*
|
||||
* // 设置当前激活语言
|
||||
* lang.setActivated("zh_CN");
|
||||
*
|
||||
* // 引用当前激活语言
|
||||
* Multilingual.text("say.msg");
|
||||
* Multilingual.text("say.msg", "timi-java");
|
||||
*
|
||||
* // 动态引用
|
||||
* Multilingual.map("en_US").text("say.msg");
|
||||
* </pre>
|
||||
*
|
||||
* @author 夜雨
|
||||
* @version 2021-10-25 15:16
|
||||
*/
|
||||
public class Multilingual implements LanguageMapping {
|
||||
|
||||
protected final Map<Language, AbstractLanguageMapper> multilingualMap;
|
||||
protected final List<CallbackArg<Language>> updateActiveListeners;
|
||||
|
||||
/** 当前激活语言 */
|
||||
protected Language activated;
|
||||
|
||||
protected boolean isDebugging = false;
|
||||
|
||||
public Multilingual() {
|
||||
multilingualMap = new HashMap<>();
|
||||
updateActiveListeners = new ArrayList<>();
|
||||
|
||||
activated = Ref.toType(Language.class, Locale.getDefault().toString());
|
||||
|
||||
if (activated == null) {
|
||||
activated = Language.zh_CN;
|
||||
}
|
||||
}
|
||||
|
||||
public void add(Language language, AbstractLanguageMapper mapper) {
|
||||
mapper.setDebugging(isDebugging);
|
||||
multilingualMap.put(language, mapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加更新激活语言回调
|
||||
*
|
||||
* @param callback 回调
|
||||
*/
|
||||
public void addUpdateActiveListeners(CallbackArg<Language> callback) {
|
||||
updateActiveListeners.add(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除更新激活语言回调
|
||||
*
|
||||
* @param callback 回调
|
||||
*/
|
||||
public void removeUpdateActiveListeners(CallbackArg<Language> callback) {
|
||||
updateActiveListeners.remove(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置激活语言
|
||||
*
|
||||
* @param languageName 激活语言
|
||||
*/
|
||||
public void setActivated(String languageName) {
|
||||
setActivated(Ref.toType(Language.class, languageName));
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置激活语言
|
||||
*
|
||||
* @param activated 激活语言
|
||||
*/
|
||||
public void setActivated(Language activated) {
|
||||
Objects.requireNonNull(activated);
|
||||
this.activated = activated;
|
||||
|
||||
String[] localSp = activated.toString().split("_");
|
||||
Locale.setDefault(new Locale(localSp[0], localSp[1]));
|
||||
|
||||
for (int i = 0; i < updateActiveListeners.size(); i++) {
|
||||
updateActiveListeners.get(i).handler(activated);
|
||||
}
|
||||
}
|
||||
|
||||
public Language getActivated() {
|
||||
return activated;
|
||||
}
|
||||
|
||||
public void setDebugging(boolean debugging) {
|
||||
isDebugging = debugging;
|
||||
for (Map.Entry<Language, AbstractLanguageMapper> item : multilingualMap.entrySet()) {
|
||||
item.getValue().setDebugging(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定映射数据
|
||||
*
|
||||
* @param lang 语言
|
||||
* @return 映射表
|
||||
*/
|
||||
public AbstractLanguageMapper map(String lang) {
|
||||
Language language = Ref.toType(Language.class, lang);
|
||||
if (language == null) {
|
||||
throw new RuntimeException("not support language: " + lang);
|
||||
}
|
||||
return map(language);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定映射数据
|
||||
*
|
||||
* @param lang 语言
|
||||
* @return 映射表
|
||||
*/
|
||||
public AbstractLanguageMapper map(Language lang) {
|
||||
return multilingualMap.get(lang);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(String key, String value) {
|
||||
multilingualMap.get(getActivated()).add(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前激活语言是否存在语言映射
|
||||
*
|
||||
* @param key 键
|
||||
* @return true 为存在
|
||||
*/
|
||||
@Override
|
||||
public boolean has(String key) {
|
||||
if (multilingualMap.get(getActivated()) == null) {
|
||||
return false;
|
||||
}
|
||||
return multilingualMap.get(getActivated()).has(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从当前激活语言获取文本,支持二次映射,没有找到映射时返回入参键
|
||||
* <pre>
|
||||
* test.msg=Hello world
|
||||
* mapping=@test.msg
|
||||
* no_mapping=\@test.msg
|
||||
*
|
||||
* Multilingual.text("mapping"); // Hello world
|
||||
* Multilingual.text("no_mapping"); // @test.msg
|
||||
* </pre>
|
||||
*
|
||||
* @param key 键
|
||||
* @return 文本值
|
||||
*/
|
||||
@Override
|
||||
public String text(String key) {
|
||||
if (multilingualMap.get(getActivated()) == null) {
|
||||
return key;
|
||||
}
|
||||
return multilingualMap.get(getActivated()).text(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从当前激活语言获取文本
|
||||
*
|
||||
* @param key 键
|
||||
* @param def 默认值(没有找到映射值时)
|
||||
* @return 获取结果
|
||||
*/
|
||||
@Override
|
||||
public String text(String key, String def) {
|
||||
if (multilingualMap.get(getActivated()) == null) {
|
||||
return Objects.requireNonNullElse(def, key);
|
||||
}
|
||||
return multilingualMap.get(getActivated()).text(key, def);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从当前激活语言获取插入参数文本
|
||||
*
|
||||
* @param key 键
|
||||
* @param params 参数
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public String textArgs(String key, Object... params) {
|
||||
if (multilingualMap.get(getActivated()) == null) {
|
||||
return key;
|
||||
}
|
||||
return multilingualMap.get(getActivated()).textArgs(key, params);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
package com.imyeyu.lang.multi;
|
||||
|
||||
import com.imyeyu.java.bean.Language;
|
||||
import com.imyeyu.lang.mapper.AbstractLanguageMapper;
|
||||
import com.imyeyu.lang.mapper.LanguageMap;
|
||||
import com.imyeyu.lang.mapper.ResourcesLanguageMap;
|
||||
|
||||
/**
|
||||
* @author 夜雨
|
||||
* @version 2024-04-09 01:03
|
||||
*/
|
||||
public class ResourcesMultilingual extends Multilingual {
|
||||
|
||||
public void addAll(String path) {
|
||||
Language[] values = Language.values();
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
ResourcesLanguageMap mapper = new ResourcesLanguageMap(values[i]);
|
||||
mapper.load(path);
|
||||
if (multilingualMap.containsKey(values[i])) {
|
||||
AbstractLanguageMapper existMapper = multilingualMap.get(values[i]);
|
||||
if (existMapper instanceof LanguageMap map) {
|
||||
map.union(mapper);
|
||||
}
|
||||
} else {
|
||||
add(values[i], mapper);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user