Préface
La société a transféré le projet de Struts2 à SpringMVC. Étant donné que les activités de l'entreprise sont des services à l'étranger, la demande de fonctions internationales est très élevée. La fonction d'internationalisation de Struts2 est plus parfaite que SpringMVC, mais la grande fonctionnalité de Spring est qu'elle est personnalisable et personnalisée, donc sa fonction d'internationalisation est ajoutée lorsque le projet de l'entreprise est transplanté à SpringMVC. J'ai compilé les enregistrements et les ai améliorés ici.
Les principales fonctions implémentées dans cet article:
Chargez directement plusieurs fichiers internationaux à partir d'un dossier. La page frontale du réglage d'arrière-plan affiche des informations internationales. Le fichier qui affiche des informations internationales est automatiquement défini à l'aide d'intercepteurs et d'annotations. Le fichier qui affiche des informations internationales sur la page frontale affiche des informations internationales.
Remarque: Cet article ne présente pas en détail comment configurer l'internationalisation, l'analyseur régional, etc.
accomplir
Initialisation du projet international
Créez d'abord un projet de base Spring-Boot + Thymeleaf + Internationalisation (Message.Properties). Si vous en avez besoin, vous pouvez le télécharger à partir de mon github.
Un bref aperçu du répertoire et des fichiers du projet
Parmi eux, I18NAPPLICATION.Java établit un CookieLocaleResolver , qui utilise des cookies pour contrôler les langues internationales. Configurez également un intercepteur LocaleChangeInterceptor pour intercepter les changements dans les langues internationales.
@ SpringbootApplication @ ConfigurationPublic classe I18NApplication {public static void main (String [] args) {SpringApplication.Run (i18napplication.class, args); } @Bean Public LocalerEResolver LocalERSolver () {CookieLocaleResolver SLR = new CookieloCaleResolver (); slr.setcookieMaxage (3600); slr.setcookiename ("langue"); // définit le nom du cookie stocké à la langue return slr; } @Bean public webmvcConfigurer webmvcConfigurer () {return new webmvcconfigurer () {// interceptor @ override public void addInterceptors (InterceptorRegistry Registry) {registre.addInterceptor (new localChangeInterceptor ()). AddPathPatterns ("/ **"); }}; }}Jetons un coup d'œil à ce qui est écrit dans Hello.html:
<! Doctype html> <html xmlns = "http://www.w3.org/1999/xhtml" xmlns: th = "http://www.thymeleaf.org"> <ead> <itle> bonjour world! </ Title> </head> <body> <H1 th: text = "# {i18n_page}"> </h1> <h3 th: text = "# {hello}"> </h3> </ body> </html> Commencez maintenant le projet et visitez http://localhost:9090/hello (j'ai défini le port sur 9090 dans Application.Properties).
Étant donné que la langue par défaut du navigateur est chinoise, il sera par défaut de rechercher dans messages_zh_cn.properties, et sinon, il recherchera dans messages.properties pour les mots internationalisés.
Ensuite, nous entrons http://localhost:9090/hello?locale=en_US dans le navigateur et la langue sera coupée en anglais. De même, si le paramètre après l'URL est défini sur les locale=zh_CH , la langue sera coupée en chinois.
Chargez plusieurs fichiers internationaux directement à partir d'un dossier
Dans notre page hello.html, il n'y a que deux informations internationales «i18n_page» et «bonjour». Cependant, dans les projets réels, il n'y aura certainement pas aussi petit que quelques informations internationales, généralement des centaines. Ensuite, nous ne devons pas mettre autant d'informations internationales dans un fichier avec messages.properties , et généralement les informations internationales sont classées et stockées dans plusieurs fichiers. Cependant, lorsque le projet deviendra plus grand, ces fichiers internationaux deviendront de plus en plus. À l'heure actuelle, il n'est pas pratique de configurer ce fichier un par un dans le fichier application.properties . Nous implémentons donc maintenant une fonction pour charger automatiquement tous les fichiers internationaux dans le répertoire de formulation.
Hériter ResourceBundleMessageSource
Créez une classe dans le cadre du projet pour hériter ResourceBundleMessageSource ou ReloadableResourceBundleMessageSource et nommez- MessageResourceExtension . Et l'injectez-le dans le haricot et nommé messageSource , où nous héritons de ressourcesbundleMessageSource.
@Component ("MessageSource") classe publique MessageResourceExtension étend ResourceBundleMessageSource {} Notez que le nom de notre composant doit être «MessageSource», car lors de l'initialisation ApplicationContext , le bean avec le bean nommé «MessageSource» sera recherché. Ce processus est dans AbstractApplicationContext.java . Jetons un coup d'œil au code source
/ *** Initialisez les messages. * Utilisez les parents si aucun n'est défini dans ce contexte. * / Protected void initMessagesource () {configurableListableBeAnfactory beanfactory = getbeanfactory (); if (beanfactory.containsLocalbean (message_source_bean_name)) {this.messagesource = beanfactory.getBean (message_source_bean_name, messagesource.class); ...}} ... Dans cette méthode d'initialisation des messages, BeanFactory recherche des haricots injectés avec le nom MESSAGE_SOURCE_BEAN_NAME(messageSource) . S'il n'est pas trouvé, il cherchera s'il y a un haricot avec le nom dans sa classe parent.
Implémentation de chargement de fichiers
Maintenant, nous pouvons démarrer MessageResourceExtension que nous venons de créer
La méthode pour charger le fichier est écrite.
@Component ("MessageSource") classe publique MessageResourceExtension étend ResourceBundleMessagesource {private final static logger Logger = LoggerFactory.getLogger (MessageResourceExtension.class); / ** * Répertoire de fichiers d'internationalisation spécifié * / @value (value = "$ {printemps.messages.basefolder: i18n}") private String BaseFolder; / ** * Fichier d'internationalisation spécifié par Parent MessageSource * / @Value (value = "$ {printemps.messages.basename: message}") Private String Basename; @PostConstruct public void init () {logger.info ("init messageResourceExtension ..."); if (! StringUtils.Isempty (BaseFolder)) {try {this.setBasenames (getAllBasenames (basefolder)); } catch (ioException e) {logger.Error (e.getMessage ()); }} // Définir le parent messagesource ResourceBundleMessageSource Parent = new ResourceBundleMessageSource (); parent.setBasename (Basename); this.setParentMessagesource (parent); } / ** * Obtenez tous les noms de fichiers internationaux dans le dossier * * @Param FolderName Nom du fichier * @return * @throws ioException * / private String [] getAllBasenames (String FolderName) lève ioException {Resource Resource = new ClassPathResource (FolderName); File file = ressource.getFile (); List <string> basenames = new ArrayList <> (); if (file.exists () && file.isDirectory ()) {this.getAllFile (Basenames, fichier, ""); } else {Logger.Error ("Le fichier de base spécifié n'existe pas ou n'est pas un dossier"); } return basenames.toArray (new String [basenames.size ()]); } / ** * Traverse tous les fichiers * * @param Basenames * @param dossier * @param path * / private void getAllFile (list <String> Basenames, fichier de fichier, Path String) {if (Folder.isDirectory ()) {for (fichier file: Folder.ListFiles ()) {this.getAllfile (basenaS, fichier, fichier, path + dardder.Getname. }} else {String i18Name = this.geti18FileName (path + folder.getName ()); if (! Basenames.Contains (i18Name)) {basenames.add (i18Name); }}} / ** * Convertir les noms de fichiers normaux en noms de fichiers internationaux * * @param filename * @return * / private String geti18FileName (String filename) {filename = filename.replace (". Properties", ""); pour (int i = 0; i <2; i ++) {int index = filename.LastIndexof ("_"); if (index! = -1) {filename = filename.substring (0, index); }} return nom de fichier; }}Expliquez plusieurs méthodes tournées à tour.
@PostConstruct sur init() , qui appellera automatiquement init() après l'instanciation de la classe MessageResourceExtension. Cette méthode obtient tous les fichiers d'internationalisation dans baseFolder et les définit sur basenameSet . Et définir un ParentMessageSource , qui appellera le parent messages pour trouver les informations internationales lorsque les informations internationales ne peuvent être trouvées.getAllBaseNames() obtient le chemin d'accès à baseFolder , puis appelle getAllFile() pour obtenir les noms de fichiers de tous les fichiers internationaux dans le répertoire.getAllFile() Traverse le répertoire, s'il s'agit d'un dossier, continue de traverser, s'il s'agit d'un fichier, appelez getI18FileName() pour convertir le nom de fichier en un nom de ressource international au format 'i18n / basename /'. Donc, dites-le simplement, une fois que MessageResourceExtension est instancié, le nom du fichier de ressources dans le dossier 'i18n' est chargé dans Basenames . Voyons maintenant l'effet.
Tout d'abord, nous ajoutons un spring.messages.baseFolder=i18n au fichier application.properties, qui attribuera la valeur 'i18n' baseFolder dans MessageResourceExtension .
Après le début, j'ai vu que les informations d'initiation ont été imprimées dans la console, indiquant que la méthode init () annotée par @PostConstruct a été exécutée.
Ensuite, nous créons deux ensembles de fichiers d'informations internationales: «Dashboard» et «Merchant», qui n'a qu'une seule information internationale: «Dashboard.hello» et «Merchant.hello» respectivement.
Modifiez ensuite le fichier hello.html, puis visitez la page Hello.
... <body> <h1> Page d'internationalisation! </h1> <p th: text = "# {hello}"> </p> <p th: text = "# {merchant.hello}"> </p> <p th: text = "# {dashboard.hello}"> </p> </ body> ...Vous pouvez voir que les informations d'internationalisation dans «message», «tableau de bord» et «marchand» sont chargées dans la page Web, indiquant que nous avons réussi à charger le fichier dans le dossier «i18n» en même temps.
Fichiers qui affichent des informations internationales sur la page frontale des paramètres d'arrière-plan
Dans la section précédente, nous avons réussi à charger plusieurs fichiers d'internationalisation et avons affiché leurs informations d'internationalisation. Mais les informations d'internationalisation dans «Dashboard.properties» sont «Dashboard.hello» et «Merchant.properties» est «Merchant.hello». Il serait donc très difficile d'écrire un préfixe pour chacun. Maintenant, je veux écrire uniquement «Hello» dans les fichiers d'internationalisation de «Dashboard» et «Merchant», mais les informations d'internationalisation dans «Dashboard» ou «Merchant» s'affichent.
Réécrivez resolveCodeWithoutArguments Méthode dans MessageResourceExtension (réécrivez resolveCode s'il existe un besoin de formatage des caractères).
@Component ("MessageSource") classe publique MessageResourceExtension étend ResourceBundleMessagesource {... String statique publique i18n_attribute = "i18n_attribute"; @Override Protected String ResolveCodewithoutArguments (code de chaîne, paramètres régionaux) {// Obtenez le nom de nom de fichier international spécifié dans la demande servletRequestAttributes atTr = (ServleTrequestAttributes) requestContexTholder.CurrentRequestAttributes (); String final i18File = (String) att.getAttribute (i18n_attribute, requestAttributes.scope_request); if (! StringUtils.Isempty (i18File)) {// Obtenez le nom de fichier international correspondant à la chaîne BasenameSet Basename = getBasenAnet (). Stream () .Filter (name -> stringUtils.endsWithIGNoreCase (name, i18File)) .FinDFirst (). Orelse (null); if (! StringUtils.Isempty (Basename)) {// Obtenez le bundle de ressources de fichier international spécifié Bundle = getResourceBundle (Basename, Locale); if (bundle! = null) {return getStringorNull (bundle, code); }}} // S'il n'y a pas de champ d'internationalisation dans le dossier I18 spécifié, Retour Null recherchera Return Null dans ParentMessagesource; } ...} Dans la méthode resolveCodeWithoutArguments que nous avons réécrit, obtenez 'i18n_attribute' de httpservletRequest (parlera de savoir où définir ceci) correspondant au nom de fichier international que nous voulons afficher. Ensuite, nous recherchons le fichier dans BasenameSet , puis obtenons la ressource via getResourceBundle , et enfin getStringOrNull pour obtenir les informations internationales correspondantes.
Ajoutons maintenant deux méthodes à notre HelloController .
@ControllerPublic classe HelloController {@getMapping ("/ Hello") Public String Index (HttpServLetRequest Request) {request.setAttribute (MessageResourceExtension.i18n_attribute, "Bonjour"); return "System / Hello"; } @GetMapping ("/ Dashboard") Public String Dashboard (HttpServLetRequest Request) {request.setAttribute (MessageResourceExtension.i18n_attribute, "Dashboard"); retourner "tableau de bord"; } @GetMapping ("/ Merchant") Public String Merchant (HttpServLetRequest Request) {request.setAttribute (MessageResourceExtension.i18n_attribute, "Merchant"); retourner "marchand"; }} Découvrez que nous définissons un «i18n_attribute» correspondant dans chaque méthode, qui définira le fichier d'internationalisation correspondant dans chaque demande, puis l'obtiendra dans MessageResourceExtension .
Pour le moment, nous examinons notre fichier d'internationalisation et nous pouvons voir que tous les mots clés sont «bonjour», mais les informations sont différentes.
Dans le même temps, deux nouveaux fichiers HTML sont 'Dashboard.html' et 'Merchant.html', qui ne dispose que d'une information internationale pour 'Hello' et d'un titre pour la distinction.
<! - Ceci est bonjour.html -> <body> <h1> Page d'internationalisation! </h1> <p th: text = "# {hello}"> </p> </ body> <! - Ceci est Dashboard.html -> <body> <h1> Page d'internationalisation (tableau de bord)! </h1> <p th: text = "# {hello}"> </p> </odyfing> <! - Ceci est marchand.html -> <body> <h1> Page d'internationalisation (marchand)! </h1> <p th: text = "# {hello}"> </p> </ body>Pour le moment, commençons le projet et jetons un coup d'œil.
Vous pouvez voir que bien que le mot d'internationalisation sur chaque page soit «bonjour», nous affichons les informations que nous voulons afficher sur la page correspondante.
Utilisez des intercepteurs et des annotations pour configurer automatiquement des fichiers qui affichent des informations internationales sur la page frontale
Bien que les informations d'internationalisation correspondantes puissent être spécifiées, il est trop gênant de définir le fichier d'internationalisation dans le HTTPServleRequest dans chaque contrôleur, nous implémentons maintenant le jugement automatique pour afficher le fichier correspondant.
Nous créons d'abord une annotation, qui peut être placée sur une classe ou une méthode.
@Target ({elementType.Type, elementType.Method}) @ Rétention (RetentionPolicy.Runtime) public @Interface i18n {/ ** * Nom de fichier internationalisé * / Valeur de chaîne ();} Ensuite, nous avons mis l'annotation I18n créée dans la méthode du contrôleur tout à l'heure. Afin d'afficher son effet, nous créons un ShopController et UserController , et créons également les fichiers internationaux «Shop» et «utilisateur» correspondants, et le contenu est également un «bonjour».
@ControllerPublic classe HelloController {@getMapping ("/ Hello") public String index () {return "System / Hello"; } @ I18n ("Dashboard") @getMapping ("/ Dashboard") Public String Dashboard () {return "Dashboard"; } @ I18n ("marchand") @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"; }} Nous plaçons I18n sous dashboard et les méthodes merchant sous HelloController et sur ShopController respectivement. Et l'instruction qui définit 'i18n_attribute' sous le dashboard d'origine et les méthodes merchant est supprimée.
Les préparatifs sont tous effectués, voyez maintenant comment spécifier automatiquement les fichiers d'internationalisation en fonction de ces annotations.
classe publique MessageResourceInterceptor implémente HandlerInterceptor {@Override public void Posthandle (httpServletRequest req, httpservletResponse rep, objet handler, modelandView ModelandView) {// Définit le chemin d'i18 dans la méthode if (null! = req.getAttribute (MessageRereSourceCeExtension.i18n_atTrut); } HandLerMethod Method = (handlerMethod) Handler; // Annotation sur la méthode i18 i18n i18nMethod = method.getMethodannotation (i18n.class); if (null! = i18nMethod) {req.setAttribute (MessageResourceExtension.i18n_attribute, i18nMethod.Value ()); retour; } // Annotation sur le contrôleur I18N I18NController = Method.getBeantype (). GetAnnotation (i18n.class); if (null! = i18nController) {req.setAttribute (MessageResourceExtension.i18n_attribute, i18nController.Value ()); retour; } // set i18 string contrôleur = méthode.getBeAntype (). GetName (); int index = contrôleur.LastIndexof ("."); if (index! = -1) {contrôleur = contrôleur.substring (index + 1, contrôleur.length ()); } index = contrôleur.touppercase (). indexof ("contrôleur"); if (index! = -1) {contrôleur = contrôleur.substring (index + 1, contrôleur.length ()); } index = contrôleur.touppercase (). indexof ("contrôleur"); if (index! = -1) {contrôleur = contrôleur.substring (0, index); } req.setAttribute (MessageResourceExtension.i18n_attribute, contrôleur); } @Override Public Boolean Prehandle (HttpServletRequest Req, HttpServletResponse Rep, Handler d'objet) {// Lorsque vous sautez sur cette méthode, effacez d'abord les informations d'internationalisation dans la demande req.reMoveAttribute (MessageResourceExtension.i18n_Attribute); Retour Vrai; }}Permettez-moi d'expliquer brièvement cet intercepteur.
Premièrement, s'il y a déjà «i18n_attribute» dans la demande, cela signifie que les paramètres sont spécifiés dans la méthode du contrôleur et que le jugement n'est plus porté.
Déterminez ensuite s'il y a une annotation I18n sur la méthode pour entrer dans l'intercepteur. S'il y a, définissez «i18n_attribute» dans la demande et sortez de l'intercepteur. S'il n'y a pas, continuez.
Déterminez ensuite s'il y a une annotation I18n sur la classe qui est entrée dans l'ordonnée. S'il y a, définissez «i18n_attribute» dans la demande et sortez de l'intercepteur. S'il n'y a pas, continuez.
Enfin, s'il n'y a pas d'annotation I18n sur la méthode et la classe, nous pouvons automatiquement définir le fichier d'internationalisation spécifié en fonction du nom du contrôleur. Par exemple, «userController», nous rechercherons le fichier d'internationalisation «utilisateur».
Maintenant, exécutons-le à nouveau pour voir l'effet et voir le contenu de leurs informations internationales correspondantes affichées sur chaque lien.
enfin
Je viens de remplir les fonctions de base de toute notre amélioration de l'internationalisation. Enfin, j'ai trié tout le code et Bootstrap4 intégré pour afficher l'effet d'implémentation de la fonction.
Pour un code détaillé, veuillez consulter le code de Spring-Boot-I18N-Pro sur GitHub
Ce qui précède est tout le contenu de cet article. J'espère que cela sera utile à l'apprentissage de tous et j'espère que tout le monde soutiendra davantage Wulin.com.