Предисловие
Компания перенесла проект от Struts2 в Springmvc. Поскольку бизнес компании являются зарубежными услугами, спрос на международные функции очень высок. Функция интернационализации Struts2 более идеальна, чем SpringMVC, но большая особенность Spring заключается в том, что он настраивается и настроена, поэтому его функция интернационализации добавляется, когда проект компании пересаживается до SpringMVC. Я собрал записи и улучшил их здесь.
Основные функции, реализованные в этой статье:
Прямо загрузите несколько международных файлов из папки. Фоновая настройка фронтальной страницы отображает международную информацию. Файл, который отображает международную информацию, автоматически устанавливается с использованием перехватчиков и аннотаций. Файл, который отображает международную информацию на линейной странице, отображает международную информацию.
Примечание. Эта статья не введена подробно, как настроить интернационализацию, региональный анализатор и т. Д.
выполнить
Международная инициализация проекта
Сначала создайте базовый проект Spring-Boot+Thymeleaf+Internationalization (Message.Properties). Если вам это нужно, вы можете скачать его с моего GitHub.
Краткий взгляд на каталог и файлы проекта
Среди них I18NApplication.java устанавливает CookieLocaleResolver , который использует файлы cookie для контроля международных языков. Также установите LocaleChangeInterceptor перехватчика Interceptor для перехвата изменений в международных языках.
@SpringBootApplication@configurationPublic Class I18NApplication {public static void main (string [] args) {SpringApplication.run (i18napplication.class, args); } @Bean public localeresolver localeresolver () {cookielocaleresolver slr = new cookielocaleresolver (); slr.setcookiemaxage (3600); slr.setcookiename ("language"); // Установить имя хранимого cookie на язык return slr; } @Bean public webmvcconfigurer webmvcconfigurer () {return new webmvcconfigurer () {// receptor @replide public void addinterceptors (registry registry) {registry.addinterceptor (new localechangeinterceptor ()). Addpathpathterns ("/**"); }}; }}Давайте посмотрим на то, что написано в hello.html:
<! Doctype html> <html xmlns = "http://www.w3.org/1999/xhtml" xmlns: th = "http://www.thymelef.org"> <Head> <title> Hello World! th: text = "#{i18n_page}"> </h1> <h3 th: text = "#{hello}"> </h3> </body> </html> Теперь запустите проект и посетите http://localhost:9090/hello (я установил порт на 9090 в Application.properties).
Поскольку язык по умолчанию браузера является китайским, он по умолчанию будет искать в сообщении_ZH_CN.Properties, а если нет, он будет искать в сообщениях.
Затем мы вводим http://localhost:9090/hello?locale=en_US в браузере, и язык будет вырезан на английский язык. Точно так же, если параметр после URL установлен на locale=zh_CH , язык будет вырезан на китайский.
Загрузите несколько международных файлов непосредственно из папки
На нашей странице hello.html есть только две международные данные «i18n_page» и «hello». Тем не менее, в реальных проектах определенно будет не такая небольшая, как несколько международных информации, обычно сотни из них. Затем мы не должны помещать так много международной информации в файл с messages.properties . Однако, когда проект станет больше, эти международные файлы станут все больше и больше. В настоящее время неудобно настроить этот файл один за другим в файле application.properties . Итак, теперь мы реализуем функцию для автоматической загрузки всех международных файлов в каталоге формулировки.
Унаследовать Resourcebundlemessagesource
Создайте класс в рамках проекта, чтобы унаследовать ResourceBundleMessageSource или ReloadableResourceBundleMessageSource и назвать его MessageResourceExtension . И введите его в бобов и названный messageSource , где мы наследуем Resourcebundlemessagesource.
@Component ("MessageSource") открытый класс MessagerSourceExtension расширяет ресурсы bundlemessagesource {} Обратите внимание, что наше имя компонента должно быть «MessageSource», потому что при инициализации ApplicationContext , боб с бобов с именем «MessageSource» будет рассмотрено. Этот процесс в AbstractApplicationContext.java . Давайте посмотрим на исходный код
/*** Инициализировать сообщения сообщений.*Используйте родительские, если нет, не определено в этом контексте.*/Protected void initmessagesource () {configurableListableBeanFactory BeanFactory = getBeanFactory (); if (beanfactory.containslocalbean (message_source_bean_name)) {this.messagesource = beanfactory.getbean (message_source_bean_name, messageource.class); ...}} ... В этом методе инициализации MessageSource BeanFactory ищет бобы, вводимые с именем MESSAGE_SOURCE_BEAN_NAME(messageSource) . Если не найдено, он будет искать, есть ли боб с именем в его родительском классе.
Реализовать загрузку файла
Теперь мы можем начать MessageResourceExtension мы только что создали
Метод загрузки файла записан.
@Component ("messageource") открытый класс MessagerSourceextension Extends resourceBundLemessAgesource {private final Static Logger logger = loggerFactory.getLogger (messagerSourceextension.class); / *** Указанный каталог файлов интернационализации*/ @Value (value = "$ {spring.messages.baseFolder: i18n}") private String Basefolder; / *** Файл интернационализации, указанный Parent MessageSource*/ @Value (value = "$ {spring.messages.baseName: Message}") частная строка BaseName; @Postconstruct public void init () {logger.info ("init messagerSourceExtension ..."); if (! stringutils.isempty (basefolder)) {try {this.setbasenames (getallbasenames (basefolder)); } catch (ioException e) {logger.error (e.getMessage ()); }} // Установить родительские сообщения resourcebundlemessagesource parent = new Resourcebundlemessagesource (); parent.setbaseName (базовое имя); this.setParentMessagesource (родитель); } / ** * Получить все международные имена файлов в папке * * @param folderme name * @return * @Throws ioException * / private string [] getAllbaseNames (String folderme) Throws ioException {resource resource = new classpathresource (foldername); File file = resource.getFile (); Список <string> baseNames = new ArrayList <> (); if (file.exists () && file.isdirectory ()) {this.getallfile (baseNames, file, ""); } else {logger.error ("Указанный BaseFile не существует или не является папкой"); } вернуть baseNames.toarray (new String [baseNames.size ()]); } / ** * Траверсировать все файлы * * @param baseNames * @param folder * @param path * / private void getallfile (list <string> baseNames, file folder, string path) {if (folder.isdirectory ()) {for (file file: folder.listfiles ()) {this.getAllfile (basenames, pail + getSeparator (paliePale.getSeplies.getSeplese.getSeplese.getSeplies.getSepleser (); }} else {string i18name = this.geti18filename (path + forter.getName ()); if (! basenames.contains (i18name)) {baseNames.add (i18name); }}} / ** * Преобразовать нормальные имена файлов в международные имена файлов * * @param filename * @return * / private String geti18filename (String filename) {filename = filename.replace (". Properties", ""); for (int i = 0; i <2; i ++) {int index = filename.lastIndexof ("_"); if (index! = -1) {filename = filename.substring (0, index); }} вернуть имя файла; }}Объясните несколько методов по очереди.
@PostConstruct в методе init() , которая автоматически вызовет метод init() после создания класса MessagerSourceExtension. Этот метод получает все файлы интернационализации в каталоге baseFolder и устанавливает их в basenameSet . И установите ParentMessageSource , который позвонит родительскому сообщению, чтобы найти международную информацию, когда международная информация не может быть найдена.getAllBaseNames() получает путь к baseFolder , а затем вызывает метод getAllFile() , чтобы получить имена файлов всех международных файлов в каталоге.getAllFile() пересекает каталог, если это папка, продолжает пересекать, если это файл, вызовите getI18FileName() чтобы преобразовать имя файла в международное имя ресурса в формате I18N/BaseName/'. Поэтому проще говоря, после того, как MessageResourceExtension создается, имя файла ресурса в папке «i18n» загружено в Basenames . Теперь давайте посмотрим на эффект.
Во -первых, мы добавляем в файл spring.messages.baseFolder=i18n в baseFolder MessageResourceExtension .
После начала я увидел, что информация init была напечатана в консоли, что указывает на то, что метод init (), аннотированный @PostConstruct , был выполнен.
Затем мы создаем два набора международных информационных файлов: «Панель инструментов» и «торговец», которые имеют только одну международную информацию: «Dashboard.hello» и «Merchant.hello» соответственно.
Затем измените файл hello.html, а затем посетите страницу Hello.
... <body> <h1> страница интернационализации! </h1> <p th: text = "#{hello}"> </p> <p th: text = "#{merchant.hello}"> </p> <p th: text = "#{dashboard.hello}"> </p> </p> ... "Вы можете видеть, что информация о интернационализации в «Сообщении», «Панель инструментов» и «торговца» загружается на веб -странице, что указывает на то, что мы успешно загрузили файл в папке «i18n» за один раз.
Файлы, которые отображают международную информацию на линейной странице настройки фона
В предыдущем разделе мы успешно загрузили несколько файлов интернационализации и отобразили их информацию о интернационализации. Но информация о интернационализации в «Dashboard.Properties» - это «dashboard.hello», а «Merchant.properties» - «merchant.hello». Так что было бы очень трудно написать префикс для каждого. Теперь я хочу написать только «привет» в файлах интернационализации «Панель инструментов» и «торговца», но отображается информация о интернационализации в «Панель инструментов» или «торговца».
Переписать метод resolveCodeWithoutArguments в MessageResourceExtension (переписывайте resolveCode , если есть необходимость в форматировании символов).
@Component ("messageource") открытый класс MessagerSourceextension Extends resourceBundlemessagesource {... public Static String i18n_attribute = "i18n_attribute"; @Override защищенная строка ResolveCodeWithoutArguments (String Code, Locale Locale) {// Получить указанное международное имя файла, установленное в запросе servlectrequestattributes attr = (servletrequestattributes) requestCtholder.currentRequestattributes (); Окончательная строка i18file = (string) attr.getattribute (i18n_attribute, requestattributes.scope_request); if (! stringUtils.isempty (i18file)) {// Получить международное имя файла, сопоставленное в BaseNameset String BaseName = getBaseNameset (). Stream () .filter (name -> stringUtils.endswithignorecase (name, i18file)) .findfirst (). orelse (null); if (! stringUtils.isempty (basename)) {// Получить указанный международный ресурс -ресурс -ресурс Bundle = getResourceBundle (baseName, locale); if (bundle! = null) {return getStringornull (bundle, code); }}} // Если в указанной папке I18 нет поля интернационализации, возвращение NULL будет искать return null в ParentmessAgesource; } ...} В методе resolveCodeWithoutArguments мы переписали, получите «i18n_attribute» от httpservletrequest (расскажу о том, где установить это), соответствующее международному имени файла, которое мы хотим отобразить. Затем мы ищем файл в BasenameSet , затем получаем ресурс через getResourceBundle , и, наконец, getStringOrNull чтобы получить соответствующую международную информацию.
Теперь давайте добавим два метода в наш HelloController .
@Controllerpublic class hellocontroller {@getMaping ("/hello") public String Index (httpservletrequest) {request.setattribute (messagerSourceextension.i18n_attribute, "hello"); вернуть "System/Hello"; } @Getmapping ("/dashboard") public String Dashboard (httpservlectrequest) {request.setattribute (messagersourceextension.i18n_attribute, "Dashboard"); вернуть "панель панели"; } @GetMapping ("/merchant") public String Merchant (httpservlectrequest) {request.setattribute (messagersourceextension.i18n_attribute, "Merchant"); вернуть "продавец"; }} Посмотрите, что мы устанавливаем соответствующий «i18n_attribute» в каждом методе, который будет устанавливать соответствующий файл интернационализации в каждом запросе, а затем получить его в MessageResourceExtension .
В настоящее время мы смотрим на наш файл интернационализации и видим, что все ключевые слова - «привет», но информация отличается.
В то же время два новых файла HTML являются «Dashboard.html» и «Merchant.html», у которых есть только международная информация для «Привет» и название для различения.
<!-это hello.html-> <body> <h1> страница интернационализации! </H1> <p th: text = "#{hello}"> </p> </body> <!-Это Dashboard.html-> <body> <h1> страница интернационализации (панель панели)! </H1> <p th: text = "#{hello}"> </p> </body> <!-Это Merchant.html-> <body> <h1> страница интернационализации (торговец)! </H1> <p th: text = "#{hello}"> </p> </body>В настоящее время давайте начнем проект и посмотрим.
Вы можете видеть, что, хотя слово интернационализации на каждой странице - «Привет», мы отображаем информацию, которую хотим отобразить на соответствующей странице.
Используйте перехватчики и аннотации для автоматической настройки файлов, которые отображают международную информацию на линейной странице
Хотя соответствующая информация о интернационализации может быть указана, слишком трудно устанавливать файл интернационализации в httpservletrequest в каждом контроллере, поэтому теперь мы реализуем автоматическое суждение для отображения соответствующего файла.
Сначала мы создаем аннотацию, которая может быть размещена на классе или метод.
@Target ({elementType.type, elementtype.method}) @hareveration (hestumentpolicy.runtime) public @Interface i18n { / *** Internationalized Имя файла* / string value ();} Затем мы поместили аннотацию I18n , созданную в методе контроллера. Чтобы отобразить его эффект, мы создаем ShopController и UserController , а также создаем соответствующие международные файлы «Shop» и «пользователя», а контент также является «привет».
@Controllerpublic class hellocontroller {@getmapping ("/hello") public String index () {return "System/hello"; } @I18n ("Dashboard") @getmapping ("/Dashboard") public String Dashboard () {return "Dashboard"; } @I18n ("Merchant") @getmapping ("/merchant") public String merchant () {return "Merchant"; }} @I18n ("shop") @controllerpublic class shopcontroller {@getmapping ("shop") public String shop () {return "Shop"; }} @Controllerpublic class usercontroller {@getmapping ("user") public String user () {return "user"; }} Мы размещаем аннотацию I18n под dashboard и merchant методы под руководством HelloController и в классе ShopController соответственно. И утверждение, которое устанавливает «i18n_attribute» под исходной dashboard и merchant методами, удаляется.
Все подготовки выполняются, теперь посмотрите, как автоматически указать файлы интернационализации на основе этих аннотаций.
Открытый класс MessagerSourceInterceptor реализует HandlerInterceptor {@Override public void poshandle (httpservletrequest req, httpservletresponse rep, обработчик объекта, модель и модели модели и {// установить путь i18 в методе if (null! } Handlermethod Method = (Handlermethod) Handler; // аннотация по методу i18 i18n i18nmethod = method.getMethodannotation (i18n.class); if (null! = i18nmethod) {req.setattribute (messagerSourceExtension.i18n_attribute, i18nmethod.value ()); возвращаться; } // аннотация на контроллере i18n i18ncontroller = method.getbeantype (). getannotation (i18n.class); if (null! = i18ncontroller) {req.setattribute (messagerSourceExtension.i18n_attribute, i18ncontroller.value ()); возвращаться; } // set i18 String Controller = method.getBeantype (). GetName (); int index = controller.lastindexof ("."); if (index! = -1) {controller = controller.substring (index + 1, controller.length ()); } index = controller.touppercase (). Indexof ("Controller"); if (index! = -1) {controller = controller.substring (index + 1, controller.length ()); } index = controller.touppercase (). Indexof ("Controller"); if (index! = -1) {controller = controller.substring (0, index); } req.setattribute (messagerSourceextension.i18n_attribute, controller); } @Override public boolean prehandle (httpservlectrequest req, httpservletresponse rep, обработчик объекта) {// При прыжках с этим методом сначала очистите информацию о интернационализации в запросе req.removeattribute (messagersourceextension.i18n_attribute); вернуть истину; }}Позвольте мне кратко объяснить этот перехватчик.
Во -первых, если в запросе уже есть «i18n_attribute», это означает, что настройки указаны в методе контроллера, и больше нет суждений.
Затем определите, есть ли аннотация I18n на методе для ввода перехватчика. Если есть, установите 'i18n_attribute' в запрос и выйдите из перехвата. Если нет, продолжайте.
Затем определите, есть ли аннотация I18n в классе, который вступил в перехват. Если есть, установите 'i18n_attribute' в запрос и выйдите из перехвата. Если нет, продолжайте.
Наконец, если в методе и классе не существует аннотации I18n , мы можем автоматически установить указанный файл интернационализации в соответствии с именем контроллера. Например, «usercontroller», затем мы будем искать файл интернационализации «пользователя».
Теперь давайте запустим его снова, чтобы увидеть эффект и увидеть контент в соответствующей международной информации, отображаемой по каждой ссылке.
наконец
Я только что завершил основные функции всего нашего улучшения интернационализации. Наконец, я разобрал весь код и интегрированный Bootstrap4, чтобы показать эффект реализации функции.
Для получения подробного кода, см. Код Spring-Boot-I18N-Pro на GitHub
Выше всего содержание этой статьи. Я надеюсь, что это будет полезно для каждого обучения, и я надеюсь, что все будут поддерживать Wulin.com больше.