[Abp 源码分析]多语言(本地化)处理

点击上方蓝字关注我们

0.简介

如果你所开发的需要走向世界的话,那么肯定需要针对每一个用户进行不同的本地化处理,有可能你的客户在日本,需要使用日语作为显示文本,也有可能你的客户在美国,需要使用英语作为显示文本。如果你还是一样的写死错误信息,或者描述信息,那么就无法做到多语言适配。

Abp 框架本身提供了一套多语言机制来帮助我们实现本地化,基本思路是 Abp 本身维护一个键值对集合。只需要将展示给客户的文字信息处都使用一个语言 Key 来进行填充,当用户登录系统之后,会取得当前用户的区域文化信息进行文本渲染。

0.1 如何使用

我们首先来看一下如何定义一个多语言资源并使用。首先 Abp 自身支持三种类型的本地化资源来源,第一种是 XML 文件,第二种则是 JSON 文件,第三种则是内嵌资源文件,如果这三种都不能满足你的需求,你可以自行实现 ILocalizationSource  接口来返回多语言资源。

小提示:

Abp Zero 模块就提供了数据库持久化存储多语言资源的功能。

0.1.1 定义应用程序支持的语言

如果你需要为你的应用程序添加不同语言的支持,就必须在你任意模块的预加载方法当中添加语言来进行配置:

Configuration.Localization.Languages.Add(new LanguageInfo("en", "English", "famfamfam-flag-england", true));
Configuration.Localization.Languages.Add(new LanguageInfo("tr", "Türkçe", "famfamfam-flag-tr"));

例如以上代码,就能够让我们的程序拥有针对英语与土耳其语的多语言处理能力。

这里的 famfamfam-flag-england 与 famfamfam-flag-tr 是一个 CSS 类型,是 Abp 为前端展示所封装的小国旗图标。

0.1.2 建立多语言资源文件

有了语言之后,Abp 还需要你提供标准的多语言资源文件,这里我们以 自带的 XML 资源文件为例,其文件名称为 Abp-zh-Hans.xml ,路径为 Abp\Localization\Sources\AbpXmlSource

<?xml version="1.0" encoding="utf-8" ?>
<localizationDictionary culture="zh-Hans"><texts><text name="SmtpHost">SMTP主机</text><text name="SmtpPort">SMTP端口</text><text name="Username">用户名</text><text name="Password">密码</text><text name="DomainName">域名</text><text name="UseSSL">使用SSL</text><text name="UseDefaultCredentials">使用默认验证</text><text name="DefaultFromSenderEmailAddress">默认发件人邮箱地址</text><text name="DefaultFromSenderDisplayName">默认发件人名字</text><text name="DefaultLanguage">预设语言</text><text name="ReceiveNotifications">接收通知</text><text name="CurrentUserDidNotLoginToTheApplication">当前用户没有登录到系统!</text><text name="TimeZone">时区</text><text name="AllOfThesePermissionsMustBeGranted">您没有权限进行此操作,您需要以下权限: {0}</text><text name="AtLeastOneOfThesePermissionsMustBeGranted">您没有权限进行此操作,您至少需要下列权限的其中一项: {0}</text><text name="MainMenu">主菜单</text></texts>
</localizationDictionary>

每个文件内部,会有一个 <localizationDictionary culture="zh-Hans"> 节点用于说明当前文件是针对于哪个区域适用的,而在其 <texts> 内部则就是结合键值对的形式,name 里面的内容就是多语言文本项的键,在标签内部的就是其真正的值。

打开一个针对俄语国家的 XML 资源文件,文件名称叫做 Abp-ru.xml。

<?xml version="1.0" encoding="utf-8" ?>
<localizationDictionary culture="ru"><texts><text name="SmtpHost">SMTP сервер</text><text name="SmtpPort">SMTP порт</text><text name="Username">Имя пользователя</text><text name="Password">Пароль</text><text name="DomainName">Домен</text><text name="UseSSL">Использовать SSL</text><text name="UseDefaultCredentials">Использовать учетные данные по умолчанию</text><text name="DefaultFromSenderEmailAddress">Электронный адрес отправителя по умолчанию</text><text name="DefaultFromSenderDisplayName">Имя отправителя по умолчанию</text><text name="DefaultLanguage">Язык по умолчанию</text><text name="ReceiveNotifications">Получать уведомления</text><text name="CurrentUserDidNotLoginToTheApplication">Текущий пользователь не вошёл в приложение!</text></texts>
</localizationDictionary>

可以看到 Key 值都是一样的,只是其 <text> 内部的值根据区域国家的不同值不一样而已。

其次从文件名我们就可以看到需要使用 XML 资源文件对于文件的命名格式会有一定要求,还是以 Abp 自带的资源文件为例,可以看一下他们基本上都是由 {SourceName}-{CultureInfo}.xml 这样构成的。

0.1.3 注册本地化的 XML 资源

那么如果我们需要注册之前的两个 XML 资源到 Abp 框架当中的话,则需要在预加载模块处通过如下代码来执行注册,并且需要右键 XML 文件,更改其构建操作为 内嵌资源。

Configuration.Localization.Sources.Add(new DictionaryBasedLocalizationSource(// 本地化资源名称AbpConsts.LocalizationSourceName,// 数据源提供者,这里使用的是 XML ,除了 XML 提供者,还有 JSON 等new XmlEmbeddedFileLocalizationDictionaryProvider(typeof(AbpKernelModule).GetAssembly(), "Abp.Localization.Sources.AbpXmlSource")));

0.1.4 获取多语言文本

如果你需要在某处获取指定 Key 所对应的具体显示文本,只需要注入 ILocalizationManager ,通过其 GetString() 方法就可以获得具体的值。如果你需要获取本地化资源的地方不能够使用依赖注入,你可以使用 LocalizationHelper 静态类来进行操作。

var @string = _localizationManager.GetString("Abp", "MainMenu");

它默认是从 Thread.CurrentThread.CurrentUICulture 获取到的当前区域信息,从而来取得某个 Key 所对应的显示值,而当前区域信息是由 Abp 注入的一系列 RequestCultureProviders 所提供的,他按照以下顺序来进行设置。

  1. QueryStringRequestCultureProvider(ASP .NET Core 默认提供):该默认提供器使用的是 QueryString 的 culture&ui-culture 所提供的区域文化信息来初始化该值,例如:culture=es-MX&ui-culture=es-MX

  2. AbpUserRequestCultureProvider (Abp 提供):该提供器会读取当前用户的 IAbpSession 信息,并且从 ISettingManager 中获取用户所配置的 "Abp.Localization.DefaultLanguageName" 属性,将其作为默认的区域文化信息。

  3. **AbpLocalizationHeaderRequestCultureProvider ** (Abp 提供):使用每次请求头当中的 .AspNetCore.Culture 值作为当前的区域文化信息,例如 c=en|uic=en-US

  4. CookieRequestCultureProvider (ASP .NET Core 提供):使用每次请求的 Cookie 当中 Key 为 .AspNetCore.Culture 值作为当前区域文化信息。

  5. AbpDefaultRequestCultureProvider (Abp 提供):如果之前这些提供器都没有为当前区域文化赋值,则从 ISettingMananger 当中取得 Abp.Localization.DefaultLanguageName 的默认值。

  6. AcceptLanguageHeaderRequestCultureProvider (ASP .NET Core 默认提供):该提供器最终会使用用户每次请求时传递的 Accept-Language 头部作为当前区域文化信息。

小提示:

这里 Abp 注入的提供器是有顺序的,注入这么多提供器就是为了最后确定当前用户的区域文化信息以便展示相应的语言文本。

1.启动流程

1.1 启动流程图

1.2 代码流程

根据使用方法我们可以得知,要配置 Abp 的多语言,必须得等 IAbpStartupConfiguration 初始化完毕才可以。即在 AbpBootstrapper 的 Initialize() 方法之中:

public virtual void Initialize()
{// ... 其他代码// 注入 IAbpStartupConfiguration 配置与本地化资源配置IocManager.IocContainer.Install(new AbpCoreInstaller());// ... 其他代码// 初始化 AbpStartupConfiguration 类型IocManager.Resolve<AbpStartupConfiguration>().Initialize();// ... 其他代码
}

配置类里面包含了用户所配置的所有语言与多语言资源信息,在被成功注入到 Ioc 容器之后,Abp 就开始使用本地化资源管理器来初始化这些多语言数据了。

public override void PostInitialize()
{// 注册缺少的组件,防止遗漏注册组件RegisterMissingComponents();IocManager.Resolve<SettingDefinitionManager>().Initialize();IocManager.Resolve<FeatureManager>().Initialize();IocManager.Resolve<PermissionManager>().Initialize();// 重点在这里,这个 PostInitialize 方法是存放在核心模块当中的,在这里调用了本地化资源管理器的初始化方法IocManager.Resolve<LocalizationManager>().Initialize();IocManager.Resolve<NotificationDefinitionManager>().Initialize();IocManager.Resolve<NavigationManager>().Initialize();if (Configuration.BackgroundJobs.IsJobExecutionEnabled){var workerManager = IocManager.Resolve<IBackgroundWorkerManager>();workerManager.Start();workerManager.Add(IocManager.Resolve<IBackgroundJobManager>());}
}

具体 LocalizationManager 及其内部的实现我们在下一节代码分析中详细进行讲述。

这些动作仅仅是在注入 Abp 框架的时候所需要执行的一些步骤,如果你要启用多语言,需要在 ASP .NET Core 程序的 Startup 类中的 Configure() 处通过更改 UseAbpRequestLocalization 状态为 True,才会将区域文化识别中间件注入到程序当中。

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{app.UseAbp(options =>{options.UseAbpRequestLocalization = false; //disable automatic adding of request localization});//...authentication middleware(s)app.UseAbpRequestLocalization(); //manually add request localization//...other middlewaresapp.UseMvc(routes =>{//...});
}

其实这里的 UseAbpRequestLocalization() 就已经将上文说的那些 RequestProvider 按照顺序依次注入到 MVC 之中了。

2.代码分析

Abp 框架针对本地化处理相关的类型与方法定义都存放在 Abp 库的 Localization 文件夹下。关系还是相对复杂的,这里我们先从其核心的 Abp 库针对于多语言的处理开始讲起。

2.1 多语言模块配置

Abp 需要使用的所有信息都是由用户在自己启动模块的 PreInitialize() 当中,通过 ILocalizationConfiguration 进行注入配置。也就是说在 ILocalizationConfiguration 内部,主要是包含了语言,与多语言资源提供者两种重点信息。

public interface ILocalizationConfiguration
{// 当前应用程序可配置的语言列表IList<LanguageInfo> Languages { get; }// 本地化资源列表ILocalizationSourceList Sources { get; }// 是否启用多语言(本地化) 系统bool IsEnabled { get; set; }// 以下四个布尔类型的参数主要用于确定当没有找到多语言文本时的处理逻辑,默认都为 Truebool ReturnGivenTextIfNotFound { get; set; }bool WrapGivenTextIfNotFound { get; set; }bool HumanizeTextIfNotFound { get; set; }bool LogWarnMessageIfNotFound { get; set; }
}

2.2 语言信息

当前应用程序能够支持哪一些语言,取决于用户在预加载的时候给多语言模块配置对象分配了哪些语言。通过第 0.1.1 节我们看到用户可以直接通过初始化一个新的 LanguageInfo 对象,将其添加到 Languages 属性之中。

public class LanguageInfo
{/// <summary>/// 区域文化代码名称/// 应该是一个有效的区域文化代码名称,更多的可以通过 CultureInfo 静态类获得所有文化代码。/// 例如: "en-US" 是北美适用的, "tr-TR" 适用于土耳其。/// </summary>public string Name { get; set; }/// <summary>/// 该语言默认应该展示的语言名称。/// 例如: 英语应该展示为 "English", "zh-Hans" 应该展示为 "简体中文"/// </summary>public string DisplayName { get; set; }/// <summary>/// 用于展示的图标 CSS 类名,可选参数/// </summary>public string Icon { get; set; }/// <summary>/// 是否为默认语言/// </summary>public bool IsDefault { get; set; }/// <summary>/// 该语言是否被禁用/// </summary>public bool IsDisabled { get; set; }/// <summary>/// 语言的展示方式是自左向右还是自右向左/// </summary>public bool IsRightToLeft{get{try{return CultureInfo.GetCultureInfo(Name).TextInfo?.IsRightToLeft ?? false;}catch{return false;}}}public LanguageInfo(string name, string displayName, string icon = null, bool isDefault = false, bool isDisabled = false){Name = name;DisplayName = displayName;Icon = icon;IsDefault = isDefault;IsDisabled = isDisabled;}
}

关于语言的定义还是相当简单的,主要参数就是语言的 区域文化代码 与 展示的名称,其余的都可以是可选参数。

小提示:

关于当前系统所支持的区域文化代码,可以通过执行 CultureInfo.GetCultures(CultureTypes.AllCultures); 得到。

2.3 语言管理器

Abp 针对语言也提供了一个管理器,接口叫做 ILanguageManager,定义简单,两个方法。

public interface ILanguageManager
{// 获得当前语言LanguageInfo CurrentLanguage { get; }// 获得所有语言IReadOnlyList<LanguageInfo> GetLanguages();
}

实现也不复杂,它内部的实现就是从一个 ILanguageProvider 拿取有哪一些语言数据。

private readonly ILanguageProvider _languageProvider;public IReadOnlyList<LanguageInfo> GetLanguages()
{return _languageProvider.GetLanguages();
}// 获取当前语言,其实就是获取的 CultureInfo.CurrentUICulture.Name 的信息,然后去查询语言集合。
private LanguageInfo GetCurrentLanguage()
{var languages = _languageProvider.GetLanguages();// ... 省略了的代码var currentCultureName = CultureInfo.CurrentUICulture.Name;var currentLanguage = languages.FirstOrDefault(l => l.Name == currentCultureName);if (currentLanguage != null){return currentLanguage;}// ... 省略了的代码return languages[0];
}

默认实现就是直接读取之前通过 Configuration 的 Languages 里面的数据。

在 Abp.Zero 模块还有两外一个实现,叫做 ApplicationLanguageProvider ,这个提供者则是从数据库表 ApplicationLanguage 获取的这些语言列表数据,并且这些语言信息还与租户有关,不同的租户他所能够获得到的语言数据也不一样。

public IReadOnlyList<LanguageInfo> GetLanguages()
{// 可以看到这里传入的当前登录用户的租户 Id,通过这个参数去查询的语言表数据var languageInfos = AsyncHelper.RunSync(() => _applicationLanguageManager.GetLanguagesAsync(AbpSession.TenantId)).OrderBy(l => l.DisplayName).Select(l => l.ToLanguageInfo()).ToList();SetDefaultLanguage(languageInfos);return languageInfos;
}

2.4 本地化资源

2.4.1 本地化资源列表

在多语言模块配置内部使用的是 ILocalizationSourceList 类型的一个 Sources 属性,该类型其实就是继承自 IList<ILocalizationSource> 的一个具体实现而已,一个类型为 ILocalizationSource 的集合,不过其扩展了一个

Extensions 属性用于存放扩展的多语言数据字段。

2.4.2 本地化资源

其接口定义为 ILocalizationSource ,Abp 默认为我们实现了四种本地化资源的实现。

第一个是空实现,可以跳过,第二个则是针对资源文件进行读取的的本地化资源,第三个是基于字典的的本地化资源定义,最后一个是由 Abp Zero 模块所提供的数据库版本的多语言资源定义。

首先看一下该接口的定义:

public interface ILocalizationSource
{// 本地化资源唯一的名称string Name { get; }// 用于初始化本地化资源,在 Abp 框架初始化的时候被调用void Initialize(ILocalizationConfiguration configuration, IIocResolver iocResolver);// 从当前本地化资源中获取给定关键字的多语言文本项,为用户当前语言string GetString(string name);// 从当前本地化资源中获取给定关键字与区域文化的多语言文本项string GetString(string name, CultureInfo culture);// 作用同上,只不过不存在会返回 NULLstring GetStringOrNull(string name, bool tryDefaults = true);// 作用同上,只不过不存在会返回 NULLstring GetStringOrNull(string name, CultureInfo culture, bool tryDefaults = true);// 获得当前语言所有的多语言文本项集合IReadOnlyList<LocalizedString> GetAllStrings(bool includeDefaults = true);// 获得给定区域文化的所有多语言文本项集合IReadOnlyList<LocalizedString> GetAllStrings(CultureInfo culture, bool includeDefaults = true);
}

也就可以这么来看,我们有几套本地化资源,他们通过 Name 来进行标识,如果你需要在本地化管理器获取某一套本地化资源,那么你可以直接通过 Name 来进行定位。而每一套本地化资源,自身都拥有具体的多语言数据,这些多语言数据有可能来自文件也有可能来自数据库,这取决于你具体的实现。

2.4.3 基于字典的本地化资源

最开始我们在使用范例当中,通过 DictionaryBasedLocalizationSource 来建立我们的本地化资源对象。该对象实现了 ILocalizationSource 与 IDictionaryBasedLocalizationSource 接口,内部定义了一个本地化资源字典提供器。

当调用本地化资源的 Initialize() 方法的时候,会使用具体的本地化资源字典提供器来获取数据,而这个字典提供器可以为 XmlFileLocalizationDictionaryProviderJsonEmbeddedFileLocalizationDictionaryProvider 等。

这些内部字典提供器在初始化的时候,会将自身的数据按照 语言/多语言项 的形式将多语言信息存放在一个字典之中,而这个字典又可以分为 XML、JSON 等等等等...

// 内部字典提供器
public interface ILocalizationDictionaryProvider
{// 语言/多语言项字典IDictionary<string, ILocalizationDictionary> Dictionaries { get; }// 本地化资源初始化时被调用void Initialize(string sourceName);
}

而这里的 ILocalizationDictionary 其实就是一个键值对,键关联的是多语言项的标识 KEY,例如 "Home",而 Value 就是具体的展示文本信息了。

而是用字典本地化资源对象获取数据的时候,其实也就是从其内部的字典提供器来获取数据。

例如本地化资源有一个 GetString() 方法,它内部拥有一个字典提供器 DictionaryProvider,我要获取某个 KEY 为 "Home" 所需要经过的步骤如下。

public ILocalizationDictionaryProvider DictionaryProvider { get; }public string GetString(string name)
{// 获取当前用户区域文化,标识为 "Home" 的展示文本return GetString(name, CultureInfo.CurrentUICulture);
}public string GetString(string name, CultureInfo culture)
{// 获取值var value = GetStringOrNull(name, culture);// 判断值为空的话,根据配置的要求是否抛出异常if (value == null){return ReturnGivenNameOrThrowException(name, culture);}return value;
}// 获得 KEY 关联的文本
public string GetStringOrNull(string name, CultureInfo culture, bool tryDefaults = true)
{var cultureName = culture.Name;var dictionaries = DictionaryProvider.Dictionaries;// 在这里就开始从初始化所加载完成的语言字典里面,获取具体的多语言项字典ILocalizationDictionary originalDictionary;if (dictionaries.TryGetValue(cultureName, out originalDictionary)){// 多语言项字典拿取具体的多语言文本值var strOriginal = originalDictionary.GetOrNull(name);if (strOriginal != null){return strOriginal.Value;}}if (!tryDefaults){return null;}//Try to get from same language dictionary (without country code)if (cultureName.Contains("-")) //Example: "tr-TR" (length=5){ILocalizationDictionary langDictionary;if (dictionaries.TryGetValue(GetBaseCultureName(cultureName), out langDictionary)){var strLang = langDictionary.GetOrNull(name);if (strLang != null){return strLang.Value;}}}//Try to get from default languagevar defaultDictionary = DictionaryProvider.DefaultDictionary;if (defaultDictionary == null){return null;}var strDefault = defaultDictionary.GetOrNull(name);if (strDefault == null){return null;}return strDefault.Value;
}

2.3.4 基于数据库的本地化资源

如果你有集成 Abp.Zero 模块的话,可以通过在启动模块的预加载方法编写以下代码启用 Zero 的多语言机制。

Configuration.Modules.Zero().LanguageManagement.EnableDbLocalization();

Abp.Zero 针对原有的本地化资源进行了扩展,新增的本地化资源类叫做 MultiTenantLocalizationSource,该类同语言管理器一样,是一个基于多租户实现的本地化资源,内部字典的值是从数据库当中获取的,其大体逻辑与字典本地化资源一样,都是内部维护有一个字典提供器。

在通过 EnableDbLocalization() 方法的时候就直接替换掉了 ILanguageProvider 的默认实现,并且在配置的 Sources 源里面也增加了 MultiTenantLocalizationSource 作为一个本地化资源。

2.5 本地化资源管理器

扯了这么多,让我们来看一下最为核心的 ILocalizationManager 接口,如果我们需要获取某个数据源的某个 Key 所对应的多语言值肯定是要注入这个本地化资源管理器来进行操作的。

public interface ILocalizationManager
{// 根据名称获得本地化数据源ILocalizationSource GetSource(string name);// 获取所有的本地化数据源IReadOnlyList<ILocalizationSource> GetAllSources();
}

这里的数据源标识的就是一个命名空间的作用,比如我在 A 模块当中有一个 Key 为 "Home" 的多语言项,在 B 模块也有一个 Key 为 "Home" 的多语言项,这个时候就可以用数据源标识来区分这两个 "Home" 。

本地化资源管理器通过在初始化的时候调用其 Initialize() 来初始化所有被注入的本地化资源,最后并将其放在一个字典之中,以便后续使用。

private readonly IDictionary<string, ILocalizationSource> _sources;foreach (var source in _configuration.Sources)
{// ... 其他代码_sources[source.Name] = source;source.Initialize(_configuration, _iocResolver);// ... 其他代码
}

3.结语

针对 Abp 的多语言处理本篇文章不太适合作为入门了解,其中大部分知识需要结合 Abp 源码进行阅读才能够加深理解,此文仅作抛砖引玉之用,如有任何意见或建议欢迎大家在评论当中指出。

作者:myzony

出处:https://www.cnblogs.com/myzony/p/9496490.html

公众号“码侠江湖”所发表内容注明来源的,版权归原出处所有(无法查证版权的或者未注明出处的均来自网络,系转载,转载的目的在于传递更多信息,版权属于原作者。如有侵权,请联系,笔者会第一时间删除处理!

扫描二维码

获取更多精彩

码侠江湖

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/305020.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Kaggle 发布首份数据科学从业报告 | 不及美国同行1/3,中国数据科学家平均年薪约3万美元

Kaggle 是互联网上最著名的数据科学竞赛平台之一&#xff0c;今年 3 月 8 日&#xff0c;这家机构被谷歌收购&#xff0c;6 月 6 日又宣布用户数量超过了 100 万人。互联网创业方兴未艾&#xff0c;人工智能的浪潮又接踵而来&#xff0c;而贯穿其中的数据科学则在这更迭交替中显…

mysql设置表名字为占位符_这可能是把MySQL存储引擎讲解的最清楚的一篇文章了

存储引擎是MySQL的组件&#xff0c;用于处理不同表类型的SQL操作。不同的存储引擎提供不同的存储机制、索引技巧、锁定水平等功能&#xff0c;使用不同的存储引擎&#xff0c;还可以获得特定的功能。使用哪一种引擎可以灵活选择&#xff0c;一个数据库中多个表可以使用不同引擎…

linux开发板显示横向彩虹,给 Linux 终端的输出添加彩虹特效的命令

原标题&#xff1a;给 Linux 终端的输出添加彩虹特效的命令正文如果认为Linux命令行很无聊并且没有任何乐趣&#xff0c;那么您错了&#xff0c;真实的Linux多么有趣和淘气。在本文&#xff0c;我将介绍一个名为“ lolcat ”的实用小工具&#xff0c;该实用工具在终端中产生彩虹…

Delphi 与 DirectX 之 DelphiX(46): TDIB.DoAntiAlias;

本例效果图:代码文件:unit Unit1;interfaceusesWindows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs, DIB, StdCtrls;typeTForm1 class(TForm)DXPaintBox1: TDXPaintBox;Button1: TButton;Button2: TButton;procedure Button1Click(Sender: T…

Web API实现微信公众平台开发-服务器验证

背景最近开发微信公众号相关接口&#xff0c;在这里记录下微信公众号相关各项功能的实现。先决条件1、一台可部署web服务的服务器或者云平台&#xff08;本地可以搞个花生壳域名&#xff09;。2、一个可以正常使用的微信公众账号&#xff0c;开始的时候使用它的测试号。3、Visu…

每天20分钟,只需一年,一年级学生英语听力达到六年级水平!关键是坚持一点都不难!

导读&#xff1a; 除了语文数学这种常规科目&#xff0c;最让家长们焦虑的就是英语。现在的孩子&#xff0c;英语启蒙都很早&#xff0c;但是对英语的兴趣总是开始还可以&#xff0c;越往后越没动力和兴趣&#xff0c;稍微遇到点挫折就不想坚持了。钱也花了&#xff0c;好老师也…

python爬取论坛付费内容_Python爬虫抓取论坛关键字过程解析

前言&#xff1a; 之前学习了用python爬虫的基本知识&#xff0c;现在计划用爬虫去做一些实际的数据统计功能。由于前段时间演员的诞生带火了几个年轻的实力派演员&#xff0c;想用爬虫程序搜索某论坛中对于某些演员的讨论热度&#xff0c;并按照日期统计每天的讨论量。 这个项…

相对完善的Java通过JDBC操纵mysql的例子

工具类: Code1import java.sql.Connection; 2import java.sql.DriverManager; 3import java.sql.ResultSet; 4import java.sql.SQLException; 5import java.sql.Statement; 6 7 8public final class JDBCUtils { 910 private JDBCUtils(){}11 12 private static Strin…

如何在 ASP.Net Core 中使用 File Providers

ASP.Net Core 为了便于获取文件和文件夹信息&#xff0c;监视文件变更&#xff0c; 在文件系统中提供了一个抽象层&#xff1a;File Providers&#xff0c; 这篇文章将会讨论如何使用 File Providers 。File Provider 抽象层 file prodivers 实现了 IFileProvider 接口&#xf…

对5种主流编程语言的吐槽

不可否认&#xff0c;想要成为一名优秀的程序员确实是需要掌握多种编程语言。通过这几年的自虐式学习&#xff0c;小编也慢慢的掌握了这些编程语言。接下来要为大家&#xff0c;介绍五款让人又爱又恨的编程语言&#xff01; 1.C 语言 C 语言给人的感觉&#xff0c;就是一位神秘…

WM中的OutLook开发和操作

昨天闲来无视&#xff0c;学习了一下WM的基本开发。看WM有约的那套教程心里痒痒&#xff0c;于是下载了SDK&#xff0c;看看DEMO&#xff0c;在Sample中的示例进行加工。小有一点心得。其实总的来说难度也不是很大&#xff0c;以前没有做过FORM的程序&#xff0c;都是WEB上面的…

苹果手机运行python_iPhone是卖的最好的手机?用Python照样把他玩弄鼓掌之间!

关于 iOS 的技术解读有很多&#xff0c;但是却鲜有设备可视化同步的介绍文章。本文一起了解下这个酷炫的 iOS 黑科技。我们的任务很简单——如上图所示&#xff0c;实时获取设备的当前方向。 UIDevice.current.orientation 首先&#xff0c;需要调用 beginGeneratingDeviceOrie…

这几个动图告诉你科学的神奇,看完瞬间觉得智商都提高了

生活中简单平常的事物和现象背后&#xff0c;往往有着奇妙的原理&#xff0c;赶快跟着一起来看看涨点知识吧&#xff01; 夹心雪糕的制作原理 ▼ 难怪雪糕大小&#xff0c;厚度都一模一样 原来都是从一个模子里出来的 ▼ 煎饼可以统一翻面 再也不用担心烤焦了 ▼ 冰淇淋蛋筒的制…

自定义 ocelot 中间件输出自定义错误信息

自定义 ocelot 中间件输出自定义错误信息Introocelot 中默认的 Response 中间件在出错的时候只会设置 StatusCode 没有具体的信息&#xff0c;想要展示自己定义的错误信息的时候就需要做一些自定义了&#xff0c;对 ocelot 中的 Response 中间件做了一些小改动&#xff0c;实现…

盘点小坏蛋的礼物

我们家小坏蛋一天天长大了&#xff0c;妈妈从觉得很辛苦过渡到习惯了很辛苦&#xff0c;苦中作乐的妈妈终于抽出空来写博客啦&#xff01; 为什么叫他小坏蛋呢&#xff1f;因为他吃饭不乖。没满月的时候蛮乖的&#xff0c;每顿奶都吃的很香&#xff0c;咕咚咕咚的喝下去&#x…

为什么对gRPC做负载均衡会很棘手?

在过去的几年中&#xff0c;随着微服务的增长&#xff0c;gRPC在这些较小的服务之间的相互通信中获得了很大的普及,在后台&#xff0c;gRPC使用http/2在同一连接和双工流中复用许多请求。使用具有结构化数据的快速&#xff0c;轻便的二进制协议作为服务之间的通信介质确实很有吸…

给新手程序猿的16个必备小妙招

写在前面&#xff1a; 这个文章核心并不是程序优化的具体技巧&#xff0c;而是拿到一个问题如何思考和利用工具的通用方法。比如即使我们不知道 profiler 这个东西&#xff0c;通过搜索"代码 每一行 时间"也可以很快知道有这样的工具叫做 profiler&#xff0c;并且学…

python字符串设置字体_python怎么更改字符串后几位

python更改字符串后几位的方法&#xff1a;可以利用replace()函数来实现。replace()函数可以把字符串中的旧字符串替换成新字符串&#xff0c;并返回替换后的新字符串。具体使用方法如&#xff1a;【str.replace("is", "was")】。可以利用replace()函数来修…

程序猿的双十一最佳攻略

讲个恐怖故事 一年一度虐汪“光棍节”又来了 小天拍拍&#xff08;不存在的&#xff09;胸脯告诉大家 这个节日 我陪你们 买买买&#xff01; 限时特惠专场 1 welcome 7天教你学会数学建模及Matlab编程 数学建模涉及的内容比较广泛&#xff0c;比如碎纸片问题中所涉及的图像识…

关于Word中审阅的一个问题!

前两天&#xff0c;在帮一个杂志撰稿的过程中&#xff0c;有一个关于Word审阅的问题。&#xff08;以下内容以Word2007为例&#xff09; 本身&#xff0c;审阅的应用不算困难。 我们可以为文章添加“批注”&#xff0c;或是你的上级对文章进行修改&#xff0c;他们可以开启“审…