All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.bull.javamelody.internal.common.I18N Maven / Gradle / Ivy

There is a newer version: 2.3.0
Show newest version
/*
 * Copyright 2008-2019 by Emeric Vernat
 *
 *     This file is part of Java Melody.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.bull.javamelody.internal.common;

import java.io.IOException;
import java.io.Writer;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.MessageFormat;
import java.util.Date;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.TimeZone;

import net.bull.javamelody.Parameter;

/**
 * Classe de gestion des traductions et de l'internationalisation (formats de dates et de nombre).
 * La locale pour les rapports vient de la requête et est associée au thread courant.
 * @author Emeric Vernat
 */
public final class I18N {
	// RESOURCE_BUNDLE_BASE_NAME vaut "net.bull.javamelody.resource.translations"
	// ce qui charge net.bull.javamelody.resource.translations.properties
	// et net.bull.javamelody.resource.translations_fr.properties
	// (Parameters.getResourcePath("translations") seul ne fonctionne pas si on est dans un jar/war)
	private static final String RESOURCE_BUNDLE_BASE_NAME = Parameters
			.getResourcePath("translations").replace('/', '.').substring(1);
	private static final ThreadLocal LOCALE_CONTEXT = new ThreadLocal();
	// Locale.ROOT needs 1.6
	private static final Locale ROOT_LOCALE = Locale.ROOT;

	private static final Locale FIXED_LOCALE = getFixedLocale();

	private I18N() {
		super();
	}

	/**
	 * Définit la locale (langue et formats dates et nombres) pour le thread courant.
	 * @param locale Locale
	 */
	public static void bindLocale(Locale locale) {
		LOCALE_CONTEXT.set(locale);
	}

	/**
	 * Retourne la locale pour le thread courant ou la locale par défaut si elle n'a pas été définie.
	 * @return Locale
	 */
	public static Locale getCurrentLocale() {
		if (FIXED_LOCALE != null) {
			return FIXED_LOCALE;
		}
		final Locale currentLocale = LOCALE_CONTEXT.get();
		if (currentLocale == null) {
			return Locale.getDefault();
		}
		return currentLocale;
	}

	/**
	 * Retourne les traductions pour la locale courante.
	 * @return Locale
	 */
	public static ResourceBundle getResourceBundle() {
		final Locale currentLocale = getCurrentLocale();
		if (Locale.ENGLISH.getLanguage().equals(currentLocale.getLanguage())) {
			// there is no translations_en.properties because translations.properties is in English
			// but if user is English, do not let getBundle fallback on server's default locale
			return ResourceBundle.getBundle(RESOURCE_BUNDLE_BASE_NAME, ROOT_LOCALE);
		}
		// and if user is not English, use the bundle if it exists for his/her Locale
		// or the bundle for the server's default locale if it exists
		// or default (English) bundle otherwise
		return ResourceBundle.getBundle(RESOURCE_BUNDLE_BASE_NAME, currentLocale);
	}

	/**
	 * Enlève le lien entre la locale et le thread courant.
	 */
	public static void unbindLocale() {
		LOCALE_CONTEXT.remove();
	}

	/**
	 * Retourne une traduction dans la locale courante.
	 * @param key clé d'un libellé dans les fichiers de traduction
	 * @return String
	 */
	public static String getString(String key) {
		return getResourceBundle().getString(key);
	}

	/**
	 * Retourne une traduction dans la locale courante et l'encode pour affichage en javascript.
	 * @param key clé d'un libellé dans les fichiers de traduction
	 * @return String
	 */
	public static String getStringForJavascript(String key) {
		final String string = getString(key);
		// ici, le résultat ne contient pas de valeur variable ni d'attaque puisque ce sont des messages internes et fixes,
		// donc pas besoin d'encoder avec javascriptEncode, et on conserve les apostrophes lisibles dans les messages
		return string.replace("\\", "\\\\").replace("\n", "\\n").replace("\"", "\\\"").replace("'",
				"\\'");
	}

	/**
	 * Retourne une traduction dans la locale courante et insère les arguments aux positions {i}.
	 * @param key clé d'un libellé dans les fichiers de traduction
	 * @param arguments Valeur à inclure dans le résultat
	 * @return String
	 */
	public static String getFormattedString(String key, Object... arguments) {
		// échappement des quotes qui sont des caractères spéciaux pour MessageFormat
		final String string = getString(key).replace("'", "''");
		return new MessageFormat(string, getCurrentLocale()).format(arguments);
	}

	/**
	 * Encode pour affichage en javascript.
	 * @param text message à encoder
	 * @return String
	 */
	public static String javascriptEncode(String text) {
		return text.replace("\\", "\\\\").replace("\n", "\\n").replace("\"", "%22").replace("'",
				"%27");
	}

	public static String urlEncode(String text) {
		return javascriptEncode(text);
	}

	/**
	 * Encode pour affichage en html.
	 * @param text message à encoder
	 * @param encodeSpace booléen selon que les espaces sont encodés en nbsp (insécables)
	 * @return String
	 */
	public static String htmlEncode(String text, boolean encodeSpace) {
		// ces encodages html sont incomplets mais suffisants pour le monitoring
		String result = text.replaceAll("[&]", "&").replaceAll("[<]", "<")
				.replaceAll("[>]", ">").replaceAll("[\n]", "
"); if (encodeSpace) { result = result.replaceAll(" ", " "); } return result; } /** * Écrit un texte dans un flux en remplaçant dans le texte les clés entourées de deux '#' * par leurs traductions dans la locale courante. * @param html texte html avec éventuellement des #clé# * @param writer flux * @throws IOException e */ public static void writeTo(String html, Writer writer) throws IOException { int index = html.indexOf('#'); if (index == -1) { writer.write(html); } else { final ResourceBundle resourceBundle = getResourceBundle(); int begin = 0; while (index != -1) { writer.write(html, begin, index - begin); final int nextIndex = html.indexOf('#', index + 1); final String key = html.substring(index + 1, nextIndex); writer.write(resourceBundle.getString(key)); begin = nextIndex + 1; index = html.indexOf('#', begin); } writer.write(html, begin, html.length() - begin); } } /** * Écrit un texte, puis un retour chariot, dans un flux en remplaçant dans le texte les clés entourées de deux '#' * par leurs traductions dans la locale courante. * @param html texte html avec éventuellement des #clé# * @param writer flux * @throws IOException e */ public static void writelnTo(String html, Writer writer) throws IOException { writeTo(html, writer); writer.write('\n'); } // méthodes utilitaires de formatage de dates et de nombres public static DecimalFormat createIntegerFormat() { // attention ces instances de DecimalFormat ne doivent pas être statiques // car DecimalFormat n'est pas multi-thread-safe, return new DecimalFormat("#,##0", getDecimalFormatSymbols()); } public static DecimalFormat createPercentFormat() { return new DecimalFormat("0.00", getDecimalFormatSymbols()); } private static DecimalFormatSymbols getDecimalFormatSymbols() { // optimisation mémoire (si Java 1.6) return DecimalFormatSymbols.getInstance(getCurrentLocale()); } public static DateFormat createDateFormat() { // attention ces instances de DateFormat ne doivent pas être statiques // car DateFormat n'est pas multi-thread-safe, // voir http://java.sun.com/javase/6/docs/api/java/text/DateFormat.html#synchronization return DateFormat.getDateInstance(DateFormat.SHORT, getCurrentLocale()); } public static DateFormat createDateAndTimeFormat() { return DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, getCurrentLocale()); } public static DateFormat createDurationFormat() { // Locale.FRENCH et non getCurrentLocale() car pour une durée on veut // "00:01:02" (1min 02s) et non "12:01:02 AM" final DateFormat durationFormat = DateFormat.getTimeInstance(DateFormat.MEDIUM, Locale.FRENCH); // une durée ne dépend pas de l'horaire été/hiver du fuseau horaire de Paris durationFormat.setTimeZone(TimeZone.getTimeZone("UTC")); return durationFormat; } public static String getCurrentDate() { return createDateFormat().format(new Date()); } public static String getCurrentDateAndTime() { return createDateAndTimeFormat().format(new Date()); } private static Locale getFixedLocale() { final String locale = Parameter.LOCALE.getValue(); if (locale != null) { for (final Locale l : Locale.getAvailableLocales()) { if (l.toString().equals(locale)) { return l; } } } return null; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy