wangshuai67 / i18n-spring-boot-starter

Spring Boot 国际化组件
MIT License
3 stars 1 forks source link

您好,可以交流一下吗? #1

Open feng-dan opened 9 months ago

feng-dan commented 9 months ago

您好,我想请教关于业务数据国际化的一个实现方案(需支持12种语言)。目前我面临的情况是,业务表中的某些属性需要支持国际化显示。我考虑采用注解结合AOP的方式,在读写这些属性时动态处理其对应的国际化信息。

具体设想是通过自定义注解(如@I18nField)标记需要国际化的字段,并使用AOP在业务数据的读取和写入过程中自动从数据库的国际化字典表中获取或更新对应的语言版本内容。

但在实际操作中,如何确保在插入、更新业务数据的同时,关联的国际化记录也能够正确创建和更新?另外,在查询业务数据时,如何高效地利用AOP拦截并填充相应的国际化值?

请问您是否有类似的实践案例或者对这种基于注解+AOP实现国际化属性读写的优化建议?非常期待与您交流这一主题,共同探讨可能的最佳实践方法。

config_i18n_message 表 image


CREATE TABLE `config_i18n_message`  (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `entity_id` bigint NULL DEFAULT NULL COMMENT '业务数据唯一标识',
  `field` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '国际化属性',
  `value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '国际化内容',
  `i18n` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '语种',
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `key`(`field` ASC) USING BTREE,
  INDEX `idx_i18n_messages_key_language`(`field` ASC, `value` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;

INSERT INTO `config_i18n_message` VALUES (9, 1, 'private java.lang.String com.fengdan.demoi18n.demos.web.TDevices.name', '我的设备', 'zh_CN');
INSERT INTO `config_i18n_message` VALUES (10, 1, 'private java.lang.String com.fengdan.demoi18n.demos.web.TDevices.notes', '我的备注', 'zh_CN');
feng-dan commented 9 months ago

/**
 * @author fengdan
 */
@Slf4j
@RequiredArgsConstructor(onConstructor_ = {@Autowired})
public abstract class AbstractI18nServiceImpl<T extends I18nEntity> implements I18nService<T> {
    protected final Class<T> entityClass;
    protected final JdbcTemplate jdbcTemplate;
    protected final TmsServiceApi tmsServiceApi;
    protected final I18nInfoMapper i18nInfoMapper;
    String enUsValue = I18nLanguageEnum.EN_US.getValue();
    protected abstract T saveEntity(T entity);
    protected abstract T getEntityById(Long entityId);
    protected abstract Long getEntityId(T entity);
    @Override
    public T getEntityByIdWithI18n(Long entityId, String language) {
        T entity = getEntityById(entityId);
        if (entity != null) {
            fillI18nInfo(entity, language);
        }
        return entity;
    }

    // 可以在保存实体时调用此方法自动添加国际化信息
    @Override
    public T saveWithI18n(T entity, String language) {
        // 调用子类提供的保存实体的方法
        T savedEntity = saveEntity(entity);
        Field[] fields = entity.getClass().getDeclaredFields();
        for (Field field : fields) {
            if (field.isAnnotationPresent(I18nField.class)) {
                try {
                    field.setAccessible(true);
                    Object fieldValue = field.get(savedEntity);
                    if (fieldValue != null && (!savedEntity.isI18nConfigured() || "zh_CN".equals(language))) {
                        addI18nInfo(savedEntity, field.toString(), fieldValue.toString(), language);
                    }
                } catch (IllegalAccessException e) {
                    log.error("ex:{}", e.getMessage(), e);
                }
            }
        }
        return savedEntity;
    }

    private void fillI18nInfo(T entity, String language) {
        Long entityId = getEntityId(entity);
        Field[] fields = entityClass.getDeclaredFields();
        for (Field field : fields) {
            if (field.isAnnotationPresent(I18nField.class)) {
                try {
                    field.setAccessible(true);
                    I18nInfo i18nInfo = findI18nValue(field.getName(), language, entityId);
                    if (i18nInfo != null) {
                        field.set(entity, i18nInfo.getValue());
                    }
                } catch (IllegalAccessException e) {
                    log.error("ex:{}", e.getMessage(), e);
                }
            }
        }
    }

    private I18nInfo findI18nValue(String fieldName, String language, Long entityId) {
        I18nInfoMapper mapper = new I18nInfoMapperImpl(jdbcTemplate);
        return mapper.findByEntityTypeAndFieldAndLanguage(fieldName, language, entityId).stream().findFirst().orElse(null);
    }

    /**
     * defaultTranslationToEnglish
     *
     * @param entity 对象
     * @param field  字段
     * @param value  内容
     */
    protected void defaultTranslationToEnglish(T entity, String field, String value) {
        String valueEnglish = new I18nTranslationService(tmsServiceApi).translateToEnglish(value);
        Long entityId = getEntityId(entity);
        I18nInfo i18nInfo = builder18nInfo(field, valueEnglish, entityId);
        I18nInfoMapper mapper = new I18nInfoMapperImpl(jdbcTemplate);
        mapper.insertI18nInfo(i18nInfo);
    }

    protected void addI18nInfo(T entity, String field, String value, String language) {
        Long entityId = getEntityId(entity);
        I18nInfo i18nInfo = builder18nInfo(field, value, language, entityId);
        I18nInfoMapper mapper = new I18nInfoMapperImpl(jdbcTemplate);
        mapper.insertI18nInfo(i18nInfo);
    }

    protected I18nInfo builder18nInfo(String field, String valueEnglish, Long entityId) {
        returnbuilder18nInfo(field, valueEnglish, enUsValue, entityId);
    }

    protected I18nInfo builder18nInfo(String field, String value, String language, Long entityId) {
        I18nInfo i18nInfo = new I18nInfo();
        i18nInfo.setField(field);
        i18nInfo.setValue(value);
        i18nInfo.setI18n(language);
        i18nInfo.setEntityId(entityId);
        return i18nInfo;
    }

}

目前实现核心代码,集成方改动点较大