Java Spring项目国际化(i18n)详细方法与实例


Posted in Python onMarch 20, 2020

Spring国际化概述

国际化基本规则

国际化信息”也称为“本地化信息”,一般需要两个条件才可以确定一个特定类型的本地化信息,它们分别是“语言类型”和“国家/地区的类型”。如中文本地化信息既有中国大陆地区的中文,又有中国台湾、中国香港地区的中文,还有新加坡地区的中文。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,部分语言和国家/地区的标准代码如下所示:

语言 简称
简体中文(中国) zh_CN
繁体中文(中国台湾) zh_TW
繁体中文(中国香港) zh_HK
英语(中国香港) en_HK
英语(美国) en_US
英语(英国) en_GB
英语(全球) en_WW
英语(加拿大) en_CA
英语(澳大利亚) en_AU
英语(爱尔兰) en_IE
英语(芬兰) en_FI
芬兰语(芬兰) fi_FI
英语(丹麦) en_DK
丹麦语(丹麦) da_DK
英语(以色列) en_IL
希伯来语(以色列) he_IL
英语(南非) en_ZA
英语(印度) en_IN
英语(挪威) en_NO
英语(新加坡) en_SG
英语(新西兰) en_NZ
英语(印度尼西亚) en_ID
英语(菲律宾) en_PH
英语(泰国) en_TH
英语(马来西亚) en_MY
英语(阿拉伯) en_XA
韩文(韩国) ko_KR
日语(日本) ja_JP
荷兰语(荷兰) nl_NL
荷兰语(比利时) nl_BE
葡萄牙语(葡萄牙) pt_PT
葡萄牙语(巴西) pt_BR
法语(法国) fr_FR
法语(卢森堡) fr_LU
法语(瑞士) fr_CH
法语(比利时) fr_BE
法语(加拿大) fr_CA
西班牙语(拉丁美洲) es_LA
西班牙语(西班牙) es_ES
西班牙语(阿根廷) es_AR
西班牙语(美国) es_US
西班牙语(墨西哥) es_MX
西班牙语(哥伦比亚) es_CO
西班牙语(波多黎各) es_PR
德语(德国) de_DE
德语(奥地利) de_AT
德语(瑞士) de_CH
俄语(俄罗斯) ru_RU
意大利语(意大利) it_IT
希腊语(希腊) el_GR
挪威语(挪威) no_NO
匈牙利语(匈牙利) hu_HU
土耳其语(土耳其) tr_TR
捷克语(捷克共和国) cs_CZ
斯洛文尼亚语 sl_SL
波兰语(波兰) pl_PL
瑞典语(瑞典) sv_SE
西班牙语(智利) es_CL

语言类型判断

1)基于浏览器语言

根据Request Headers中的Accept-language来判断。

2)基于客户端传参

要求客户端第一次(或者每次)传递的自定义参数值来判断,如规定传locale,值为zh-cn、en-us等内容,如果只在第一次传入则local以及timeZone先关信息要存入session或者cookie中,后面的请求语言方式则直接从两者中取,其有效时间与session和cookie设置的生命周期关联。

3)基于默认配置

当获取语言类型时没有找到对应类型时,会使用默认的语言类型。

语言类型保存

<!-- 定义本地化变更拦截器 -->
<bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" />
<!-- 定义注解URL映射处理器,所有的请求映射关联本地化拦截器,或者也可自定义该拦截器路径映射-->
<bean id="urlMapping" class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
 <property name="interceptors" ref=" localeChangeInterceptor " />
 <property name="order" value="1"></property>
</bean>

基于url

该种方式需要每次都在请求的url上带上local参数,指定该次需要的语言类型,并且该方式的local解析器需要配置,如下:

<a href="xxx.do?locale=zh_CN" rel="external nofollow" >中文</a>或<a href="xxx.do?locale=en" rel="external nofollow" >英文</a>
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver"/>

但在该配置下使用会抛Cannot change HTTP accept header - use a different locale resolution strategy异常,这是因为spring source做了限制,无法对本地的local赋值修改,解决办法如下,新建一个类MyLocaleResolver继承AcceptHeaderLocaleResolver,重写resolveLocale和setLocale方法,并将上面的localeResolver的class指向如下MyLocaleResolver类:

public class MyLocaleResolver extends AcceptHeaderLocaleResolver {
 private Locale myLocal;
 
public Locale resolveLocale(HttpServletRequest request) {
 return myLocal == null ? request.getLocale() : myLocal;
}
 
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
 myLocal = locale;
 }
}

基于session

基于session的状态保存方式只需要在第一次请求的时候指定语言类型,localResolver会将该属性保存到session中,后面的请求直接从session中获取该语言类型,该种方式的localResolver对应的类为SessionLocaleResolver,如下配置:

<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"/>

基于cookie

与session的机制类似,差异在于两者的存储和周期,鉴于安全、大小以及体验等因素的影响,实际使用中使用者更倾向于前者,该种cookie保存方式的localResolver为

<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver" />

文案数据来源

对于语言类型的资源文件,需要开发者对文案进行搜集整理,并翻译成相应的语言确定关键字key,目前大多数情况是将这些信息置于.properties文件中,在使用的时候直接访问获取,当然也可置于数据库中,但频繁的文案获取会影响服务器性能及产品体验,可结合数据字典以及缓存工具使用。

数据库

1)spring 配置方式

<!-- 默认的注解映射的支持 -->
<mvc:annotation-driven validator="validator" conversion-service="conversionService" />
<!-- 资源文件 -->
<bean id="propertiesMessageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
 <property name="basenames">
 <list>
 <value>resource</value>
 <value>validation</value>
 </list>
 </property>
</bean>
 
<bean id="databaseMessageSource" class="com.obs2.util.MessageResource">
 <property name="parentMessageSource" ref="propertiesMessageSource"/>
</bean>
 
<bean id="messageInterpolator" class="com.obs2.util.MessageResourceInterpolator">
 <property name="messageResource" ref="databaseMessageSource"/>
</bean>
 
<!-- 验证器 -->
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
 <property name="messageInterpolator" ref="messageInterpolator"/>
</bean>

这里定义了一个propertiesMessageSource,一个databaseMessageSourcer,和一个messageInterpolator。propertiesMessageSource用于读取properties文件databaseMessageSourcer用于读取数据库的数据配置,其中,有一个属性设置它的父MessageSource为propertiesMessageSource。意思是如果数据库找不到对应的数据,到properties文件当中查找。messageInterpolator是个拦截器。

2)数据库的POJO定义

@Entity
@SuppressWarnings("serial")
@Table(name="resource")
 
public class Resource implements Serializable {
 @Id
 @GeneratedValue(strategy=GenerationType.IDENTITY)
 @Column(name="resource_id")
 private long resourceId;
 
 @Column(name="name", length=50, nullable=false)
 private String name;
 
 @Column(name="text", length=1000, nullable=false)
 private String text;
 
 @Column(name="language", length=5, nullable=false)
 private String language;
 
 public long getResourceId() {
 return resourceId;
 }
 
 public void setResourceId(long resourceId) {
 this.resourceId = resourceId;
 }
 
 public String getName() {
 return name;
 }
 
 public void setName(String name) {
 this.name = name;
 }
 
 public String getText() {
 return text;
 }
 
 public void setText(String text) {
 this.text = text;
}
 
 public String getLanguage() {
 return language;
 }
 
 public void setLanguage(String language) {
 this.language = language;
 }
}

定义了一张表[resource],字段有:[resource_id]、[name]、[text]、[language]。

3)读取数据库的MessageResource类

/**
* 取得资源数据
* @author Robin
*/
public class MessageResource extends AbstractMessageSource implements ResourceLoaderAware {
 
 @SuppressWarnings("unused")
 private ResourceLoader resourceLoader;
 
 @Resource
 private ResourceService resourceService;
 /**
 * Map切分字符
 */
 protected final String MAP_SPLIT_CODE = "|";
 protected final String DB_SPLIT_CODE = "_";
 
 private final Map<String, String> properties = new HashMap<String, String>();
 
 public MessageResource() {
 reload();
 }
 
 public void reload() {
 properties.clear();
 properties.putAll(loadTexts());
 }
 
 protected Map<String, String> loadTexts() {
 Map<String, String> mapResource = new HashMap<String, String>();
 List<com.obs2.service.bean.Resource> resources = resourceService.findAll();
 for (com.obs2.service.bean.Resource item : resources) {
 String code = item.getName() + MAP_SPLIT_CODE + item.getLanguage();
 mapResource.put(code, item.getText());
 }
 
 return mapResource;
 }
 
 private String getText(String code, Locale locale) {
 String localeCode = locale.getLanguage() + DB_SPLIT_CODE + locale.getCountry();
 String key = code + MAP_SPLIT_CODE + localeCode;
 String localeText = properties.get(key);
 String resourceText = code;
 if(localeText != null) {
  resourceText = localeText;
 }else {
  localeCode = Locale.ENGLISH.getLanguage();
  key = code + MAP_SPLIT_CODE + localeCode;
  localeText = properties.get(key);
  if(localeText != null) {
  resourceText = localeText;
  }else {
  try {
   if(getParentMessageSource() != null) {
   resourceText = getParentMessageSource().getMessage(code, null, locale);
   }
  } catch (Exception e) {
   logger.error("Cannot find message with code: " + code);
  }
  }
 }
 return resourceText;
 }
 
 @Override
 public void setResourceLoader(ResourceLoader resourceLoader) {
 this.resourceLoader = (resourceLoader != null ? resourceLoader : new DefaultResourceLoader());
 }
 
 @Override
 protected MessageFormat resolveCode(String code, Locale locale) {
 String msg = getText(code, locale);
 MessageFormat result = createMessageFormat(msg, locale);
 return result;
 }
 
 @Override
 protected String resolveCodeWithoutArguments(String code, Locale locale) {
 String result = getText(code, locale);
 return result;
 }
}

主要是重载AbstractMessageSource和ResourceLoaderAware,以实现Spring MVC的MessageSource国际化调用。类中的reload()方法,我把它写到了一个ServletListener当中,让项目启动时,自动加载数据到static的map中。

4)Listener

/**
* 系统启动监听
* @author Robin
*/
public class SystemListener implements ServletContextListener {
/**
* context初始化时激发
*/
@Override
public void contextInitialized(ServletContextEvent e) {
// 取得ServletContext
ServletContext context = e.getServletContext();
WebApplicationContext applicationContext = WebApplicationContextUtils .getWebApplicationContext(context);
// 设置国际化多语言
MessageResource messageSource = applicationContext.getBean(MessageResource.class);
 messageSource.reload();
}
 
/**
* context删除时激发
*/
@Override
public void contextDestroyed(ServletContextEvent e) {
}
 
/**
* 创建一个 session时激发
* @param e
*/
 
public void sessionCreated(HttpSessionEvent e) {
}
 
/**
* 当一个 session失效时激发
* @param e
*
public void sessionDestroyed(HttpSessionEvent e) {
}
/**
* 设置 context的属性,它将激发attributeReplaced或attributeAdded方法
* @param e
*/
public void setContext(HttpSessionEvent e) {
}
 
/**
* 增加一个新的属性时激发
* @param e
*/
public void attributeAdded(ServletContextAttributeEvent e) {
}
 
/**
*删除一个新的属性时激发
* @param e
*/
 
public void attributeRemoved(ServletContextAttributeEvent e) {
 
}
 
/*
* 属性被替代时激发
* @param e
*/
public void attributeReplaced(ServletContextAttributeEvent e) {
}
}

该Listener需要加入到web.xml当中:

<!-- 系统启动监听 -->
<listener>
 <listener-class>com.obs2.util.SystemListener</listener-class>
</listener>

5)Interceptor拦截器

/**
* 拦截Annotation验证信息
* @author Robin
*
*/
 
public class MessageResourceInterpolator implements MessageInterpolator {
@Resource
private MessageResource messageResource;
 
public void setMessageResource(MessageResource messageResource) {
 this.messageResource = messageResource;
}
 
@Override
public String interpolate(String messageTemplate, Context context) {
 
String messageTemp = null;
 if(messageTemplate.startsWith("{") && messageTemplate.endsWith("}")) {
 messageTemp = messageTemplate.substring(1, messageTemplate.length() - 1);
 }else {
 return messageTemplate;
 }
 
 String[] params = (String[]) context.getConstraintDescriptor().getAttributes().get("params");
 MessageBuilder builder = new MessageBuilder().code(messageTemp);
 
 if (params != null) {
 for (String param : params) {
 builder = builder.arg(param);
 }
 
 }
 
 String result = builder.build().resolveMessage(messageResource, Locale.ENGLISH).getText();
 return result;
 
 }
 
@Override
public String interpolate(String messageTemplate, Context context, Locale locale) {
 
 String messageTemp = null;
 if(messageTemplate.startsWith("{") && messageTemplate.endsWith("}")) {
 messageTemp = messageTemplate.substring(1, messageTemplate.length() - 1);
 }else {
 return messageTemplate;
 }
 
 String[] params = (String[]) context.getConstraintDescriptor().getAttributes().get("params");
 
 MessageBuilder builder = new MessageBuilder().code(messageTemp);
 if (params != null) {
 builder = builder.args(params);
 }
 
 String result = builder.build().resolveMessage(messageResource, locale).getText();
 
 return result
 }
 
}

静态资源

<!-- 资源文件绑定器,文件名称:messages.properties(没有找到时的默认文件), messages_en.properties(英文),messages_zh_CN.properties(中文),等等-->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
 <property name="basename" value="config.messages.messages" />
 <property name="defaultEncoding" value="UTF-8"/>
 <property name="basename" value="i18n.messages"/>
 <property name="useCodeAsDefaultMessage" value="true" />
</bean>

文案获取

资源获取接口

MessageSource详解

Spring定义了访问国际化信息的MessageSource接口,并提供了几个易用的实现类。首先来了解一下该接口的几个重要方法:

1)String getMessage(String code, Object[] args, String defaultMessage, Locale locale) code

表示国际化资源中的属性名;args用于传递格式化串占位符所用的运行期参数;当在资源找不到对应属性名时,返回defaultMessage参数所指定的默认信息;locale表示本地化对象;

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

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

3)String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException

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

MessageSource类结构

MessageSource分别被HierarchicalMessageSource和ApplicationContext接口扩展,这里我们主要看一下HierarchicalMessageSource接口的几个实现类

Java Spring项目国际化(i18n)详细方法与实例

HierarchicalMessageSource接口添加了两个方法,建立父子层级的MessageSource结构,类似于前面我们所介绍的HierarchicalBeanFactory。该接口的setParentMessageSource (MessageSource parent)方法用于设置父MessageSource,而getParentMessageSource()方法用于返回父MessageSource。

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

ResourceBundleMessageSource与ReloadableResourceBundleMessageSource对比

1)通过ResourceBundleMessageSource配置资源

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

2)通过ReloadableResourceBundleMessageSource配置资源

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

3)对比

两者都是利用资源名通过getMessage()接口就可以加载整套的国际化资源文件,唯一区别在于ReloadableResourceBundleMessageSource可以定时刷新资源文件,以便在应用程序不重启的情况下感知资源文件的变化。很多生产系统都需要长时间持续运行,系统重启会给运行带来很大的负面影响,这时通过该实现类就可以解决国际化信息更新的问题。上面的配置中cacheSeconds属性让ReloadableResourceBundleMessageSource每5秒钟刷新一次资源文件(在真实的应用中,刷新周期不能太短,否则频繁的刷新将带来性能上的负面影响,一般不建议小于30分钟)。cacheSeconds默认值为-1表示永不刷新,此时,该实现类的功能就蜕化为ResourceBundleMessageSource的功能。

页面获取文案

利用Spring标签获取

引入标签库:

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>

获取文案:

<s:message code="test.app"/>
<spring:message code="main.title" />

利用JSTL标签获取

引入标签库:

<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt"%>

获取文案:

<fmt:message key="test.app"/>

Java代码中获取文案

利用MessageSource接口获取

1)自动注入

@Autowired
private MessageSource messageSource;
String s = messageSource.getMessage("SystemError", new Object[]{}, Locale.US);

2)手动bean获取

a. 获取容器

容器已经初始化:

WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();

容器没有初始化:

String[] configs = {"com/baobaotao/i18n/beans.xml"};
ApplicationContext ctx = new ClassPathXmlApplicationContext(configs);

b. 获取bean跟文案

MessageSource ms = (MessageSource) wac.getBean("myResource");
Object[] params = {"John", new GregorianCalendar().getTime()};
String str1 = ms.getMessage("greeting.common",params,Locale.US);

利用Spring容器获取

在前面的MessageSource类图结构中我们发现ApplicationContext实现了MessageSource的接口,也就是说ApplicationContext的实现类本身也是一个MessageSource对象。

将ApplicationContext和MessageSource整合起来, Spring此处的设计人为:在一般情况下,国际化信息资源应该是容器级。我们一般不会将MessageSource作为一个Bean注入到其他的Bean中,相反MessageSource作为容器的基础设施向容器中所有的Bean开放。只要我们考察一下国际化信息的实际消费场所就更能理解Spring这一设计的用意了。国际化信息一般在系统输出信息时使用,如Spring MVC的页面标签,控制器Controller等,不同的模块都可能通过这些组件访问国际化信息,因此Spring就将国际化消息作为容器的公共基础设施对所有组件开放。

既然一般情况下我们不会直接通过引用MessageSource Bean使用国际信息,那如何声明容器级的国际化信息呢? 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直接访问国际化信息:

a. 获取容器

容器已经初始化:

WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();

容器没有初始化:

String[] configs = {"com/baobaotao/i18n/beans.xml"};
ApplicationContext ctx = new ClassPathXmlApplicationContext(configs);

b. 获取bean跟文案

Object[] params = {"John", new GregorianCalendar().getTime()};
String str1 = ctx.getMessage("greeting.common",params,Locale.US);

注意事项

1)编码问题

a. 改变properties文件编码为UTF-8/GBK,然而ResourceBundleMessageSource的默认编码defaultEncoding是ISO-8859-1,需要在xml中增加一个相应属性将其改变为你需要的UTF-8/GBK之类。

b. 如果资源文件想统一使用ISO-8859-1格式,可以将原本用UTF-8写好的中文资源文件使用jdk自带的工具native2ascii将UTF-8文件和内容转为ISO-8859-1文件,其中的中文内容会使用16进制unicode编码为\u****格式:

cmd命令:

JAVA_HOME\bin\native2ascii -encoding UTF-8 messages_zh_CN.properties messages_zh_C1N.properties

本文主要讲解了Java Spring项目国际化(i18n)详细方法与实例,更多关于Java Spring项目国际化技巧请查看下面的相关链接

Python 相关文章推荐
Python格式化压缩后的JS文件的方法
Mar 05 Python
python批量导入数据进Elasticsearch的实例
May 30 Python
tensorflow: variable的值与variable.read_value()的值区别详解
Jul 30 Python
浅谈关于Python3中venv虚拟环境
Aug 01 Python
python根据url地址下载小文件的实例
Dec 18 Python
python selenium 弹出框处理的实现
Feb 26 Python
PyTorch搭建一维线性回归模型(二)
May 22 Python
pandas.cut具体使用总结
Jun 24 Python
详解Python 字符串相似性的几种度量方法
Aug 29 Python
python3 求约数的实例
Dec 05 Python
python属于解释语言吗
Jun 11 Python
opencv深入浅出了解机器学习和深度学习
Mar 17 Python
Python 自由定制表格的实现示例
Mar 20 #Python
python实现opencv+scoket网络实时图传
Mar 20 #Python
python实现同一局域网下传输图片
Mar 20 #Python
python实现udp传输图片功能
Mar 20 #Python
python实现UDP协议下的文件传输
Mar 20 #Python
python实现TCP文件传输
Mar 20 #Python
python实现FTP循环上传文件
Mar 20 #Python
You might like
php与XML、XSLT、Mysql的结合运用实现代码
2009/11/19 PHP
php实现阿拉伯数字和罗马数字相互转换的方法
2015/04/17 PHP
PHP、Java des加密解密实例
2015/04/27 PHP
PHP中检索字符串的方法分析【strstr与substr_count方法】
2017/02/17 PHP
PHP中Laravel 关联查询返回错误id的解决方法
2017/04/01 PHP
php 的多进程操作实践案例分析
2020/02/28 PHP
Js event事件在IE、FF兼容性问题
2011/01/01 Javascript
javascript实现的弹出层背景置灰-模拟(easyui dialog)
2013/12/27 Javascript
JavaScript数组常用操作技巧汇总
2014/11/17 Javascript
Javascript调用函数方法的几种方式介绍
2015/03/20 Javascript
JavaScript编写推箱子游戏
2015/07/07 Javascript
一种Javascript解释ajax返回的json的好方法(推荐)
2016/06/02 Javascript
微信小程序技巧之show内容展示,上传文件编码问题
2017/01/23 Javascript
如何把vuejs打包出来的文件整合到springboot里
2018/07/26 Javascript
使用vue重构资讯页面的实例代码解析
2019/11/26 Javascript
基于Vue中的父子传值问题解决
2020/07/27 Javascript
Python全局变量用法实例分析
2016/07/19 Python
Python实现复杂对象转JSON的方法示例
2017/06/22 Python
详解Python的三种可变参数
2019/05/08 Python
python清空命令行方式
2020/01/13 Python
pycharm全局搜索的具体步骤
2020/07/28 Python
python 写一个水果忍者游戏
2021/01/13 Python
秘鲁购物网站:Linio秘鲁
2017/04/07 全球购物
美国滑雪和滑雪板商店:Buckman
2018/03/03 全球购物
亚洲颇具影响力的男性在线购物零售商:His
2019/11/24 全球购物
监理资料员岗位职责
2014/01/03 职场文书
建筑工程毕业生自我鉴定
2014/01/14 职场文书
医学生临床实习自我评价
2014/03/07 职场文书
师范生求职自荐信
2014/06/14 职场文书
2014年小学重阳节活动策划方案
2014/09/16 职场文书
2014幼儿园中班工作总结
2014/11/10 职场文书
杭州西湖英语导游词
2015/02/03 职场文书
2015年敬老月活动总结
2015/03/27 职场文书
[有人@你]你有一封绿色倡议书,请查收!
2019/07/18 职场文书
创业计划书之校园超市
2019/09/12 职场文书
vue项目两种方式实现竖向表格的思路分析
2021/04/28 Vue.js