Il s'agit d'une étude des données locales du CLDR qui a été adoptée dans Java 1.8 (mais désactivée par défaut) et activée par défaut dans Java 9. Vous pouvez consulter CLDR sur les sites répertoriés à titre de référence, mais brièvement, il s'agit d'un projet en cours au Consortium Unicode et a différents paramètres régionaux (format de date, format de date, à travers le monde). Le nom de la devise, le nom du pays, la liste des jours, le format des nombres, etc.) sont stockés dans une base de données. Ces données sont gérées et publiées en LDML (Locale Data Markup Language) au format XML, et Java incorpore également ces données, mais pas complètement.
La motivation pour écrire cet article est Problèmes et solutions causés par la migration de l'infrastructure de compte de Nulab vers Java 9. Après avoir lu l'article, je me demandais quel type de code affecterait la partie citée ci-dessous.
** Les formats de date et de devise ont changé ** Le test automatisé a échoué en raison d'un changement de comportement d'exécution entre Java 8 et 9.
Le format de la date est internationalisé. En effet, Java 9 a changé la valeur initiale de l'extension d'internationalisation en CLDR (Common Locale Data Repository), qui est la norme de facto pour l'internationalisation définie par le Consortium Unicode (JEP 252).
environnement
référence
J'ai brièvement étudié la différence de comportement entre les versions de Java 8 (Oracle JDK) / Java 9 (OpenJDK) / Java 10 (OpenJDK).
Locale.toLanguageTag
Renvoie une balise de langue IETF BCP 47 bien formée qui représente cette locale.
Locale.getDefault().toLanguageTag(); // → (1)
new Locale("ja", "JP").toLanguageTag(); // → (2)
new Locale("ja", "JP", "JP").toLanguageTag(); // → (3)
** Résultat de sortie **
pattern | 1.8.0 | 9.0.4 | 10.0.1 |
---|---|---|---|
1 | ja-JP |
ja-JP |
ja-JP |
2 | ja-JP |
ja-JP |
ja-JP |
3 | ja-JP-u-ca-japanese-x-lvariant-JP |
ja-JP-u-ca-japanese-x-lvariant-JP |
ja-JP-u-ca-japanese-x-lvariant-JP |
Locale (Java SE 10 & JDK 10) Pour maintenir la compatibilité, deux paramètres régionaux non conformes sont traités comme un cas particulier. Ce sont ja_JP_JP et th_TH_TH.
En Java, ja_JP_JP a été utilisé pour représenter le calendrier impérial japonais avec le japonais utilisé au Japon. Ceci est maintenant représenté à l'aide de l'extension locale Unicode en spécifiant la clé locale Unicode ca (calendrier) et tapez japonais. L'extension u-ca-japanese est automatiquement ajoutée lorsque le constructeur Locale est appelé avec les arguments "ja", "JP", "JP".
Le "u-ca-japanese" mentionné dans le JavaDoc cité ci-dessus est "u", qui signifie extension de locale Unicode, et un mot clé (paire clé / type) qui remplace le comportement par défaut de la locale (calendrier dans cet exemple). ) Est une combinaison de "ca-japonais".
+----- U extension
| +--- Keyword (Key & Type)
| |
- -----------
u-ca-japanese
^^ ^^^^^^^^
| |
| +--- Type (japanese = Japanese Imperial calendar)
+------ Key (ca = Calendar algorithm)
Java 9 prend en charge deux types de clés (JEP 314: Additional Unicode Language-Tag Extensions):
Java 10 ajoute quatre choses:
Il existe une méthode Locale.forLanguageTag, mais Locale.Builder est recommandé, utilisez donc ce Builder pour générer des paramètres régionaux personnalisés. Voici un exemple de remplacement du type de devise par des dollars américains pour les paramètres régionaux japonais.
Locale locale = new Locale.Builder()
.setLocale(Locale.getDefault())
.setUnicodeLocaleKeyword("cu", "USD")
.build();
System.out.println(locale.toLanguageTag());
// → ja-JP-u-cu-usd
Currency currency = Currency.getInstance(locale);
System.out.println(currency.getCurrencyCode());
// → USD
System.out.println(currency.getDisplayName());
//→ dollar américain
System.out.println(currency.getSymbol());
// → $
double money = 123456789.12345;
NumberFormat formatter = NumberFormat.getCurrencyInstance(locale);
formatter.setMinimumFractionDigits(3);
System.out.println(formatter.format(money));
// → $123,456,789.123
Comme indiqué ci-dessous, vous pouvez remplacer le premier jour de la semaine par n'importe quel jour en spécifiant le mot clé d'extension Unicode "u-fw-xxx" comme décrit dans JavaDoc de la classe Calendar.
Calendar (Java SE 10 & JDK 10)
Calendrier Une semaine de 7 jours spécifique aux paramètres régionaux est définie à l'aide de deux paramètres: le premier jour de la semaine et le nombre minimum de jours de la première semaine (1-7). Ces chiffres sont tirés des données de ressources locales lors de la création du calendrier ou des paramètres régionaux eux-mêmes. Si la locale spécifiée contient "fw" et / ou "rg" "Extensions Unicode", le premier jour de la semaine sera récupéré en fonction de ces extensions.
Calendar calendar = Calendar.getInstance();
System.out.println(calendar.getCalendarType());
// → gregory
System.out.println(calendar.getFirstDayOfWeek());
// → 1 (Calendar.SUNDAY)
Locale locale = new Locale.Builder()
.setLocale(Locale.getDefault())
.setUnicodeLocaleKeyword("fw", "mon")
.build();
System.out.println(locale.toLanguageTag());
// → ja-JP-u-fw-mon
Calendar calendar = Calendar.getInstance(locale);
System.out.println(calendar.getCalendarType());
// → gregory
System.out.println(calendar.getFirstDayOfWeek());
// → 2 (Calendar.MONDAY)
Extension locale Unicode (ajout de u-ca-japanese)
Locale locale = new Locale("ja", "JP", "JP");
Date now = new Date();
DateFormat.getDateInstance(DateFormat.FULL, locale).format(now); // → (1)
DateFormat.getDateInstance(DateFormat.LONG, locale).format(now); // → (2)
DateFormat.getDateInstance(DateFormat.MEDIUM, locale).format(now); // → (3)
DateFormat.getDateInstance(DateFormat.SHORT, locale).format(now); // → (4)
Paramètres régionaux par défaut
Date now = new Date();
DateFormat.getDateInstance(DateFormat.FULL).format(now); // → (5)
DateFormat.getDateInstance(DateFormat.LONG).format(now); // → (6)
DateFormat.getDateInstance(DateFormat.MEDIUM).format(now); // → (7)
DateFormat.getDateInstance(DateFormat.SHORT).format(now); // → (8)
** Résultat de sortie **
pattern | 1.8.0 | 9.0.4 | 10.0.1 | 1.Différence entre 8 et 9 |
---|---|---|---|---|
1 | 25 juin 2018 | 25 juin 2018 | 25 juin 2018 | |
2 | H30.06.25 | 2018.06.25 | 2018.06.25 | Oui |
3 | H30.06.25 | 2018.06.25 | 2018.06.25 | Oui |
4 | H30.06.25 | 2018.06.25 | 2018.06.25 | Oui |
5 | 25 juin 2018 | Lundi 25 juin 2018 | Lundi 25 juin 2018 | Oui |
6 | 2018/06/25 | 25 juin 2018 | 25 juin 2018 | Oui |
7 | 2018/06/25 | 2018/06/25 | 2018/06/25 | |
8 | 18/06/25 | 2018/06/25 | 2018/06/25 | Oui |
Extension locale Unicode (ajout de u-ca-japanese)
Locale locale = new Locale("ja", "JP", "JP");
Calendar now = Calendar.getInstance(locale);
DateFormat.getDateInstance(DateFormat.FULL, locale).format(now.getTime()); // → (1)
DateFormat.getDateInstance(DateFormat.LONG, locale).format(now.getTime()); // → (2)
DateFormat.getDateInstance(DateFormat.MEDIUM, locale).format(now.getTime()); // → (3)
DateFormat.getDateInstance(DateFormat.SHORT, locale).format(now.getTime()); // → (4)
Paramètres régionaux par défaut
Calendar now = Calendar.getInstance();
DateFormat.getDateInstance(DateFormat.FULL).format(now.getTime()); // → (5)
DateFormat.getDateInstance(DateFormat.LONG).format(now.getTime()); // → (6)
DateFormat.getDateInstance(DateFormat.MEDIUM).format(now.getTime()); // → (7)
DateFormat.getDateInstance(DateFormat.SHORT).format(now.getTime()); // → (8)
** Résultat de sortie **
pattern | 1.8.0 | 9.0.4 | 10.0.1 | 1.Différence entre 8 et 9 |
---|---|---|---|---|
1 | 25 juin 2018 | 25 juin 2018 | 25 juin 2018 | |
2 | H30.06.25 | 2018.06.25 | 2018.06.25 | Oui |
3 | H30.06.25 | 2018.06.25 | 2018.06.25 | Oui |
4 | H30.06.25 | 2018.06.25 | 2018.06.25 | Oui |
5 | 25 juin 2018 | Lundi 25 juin 2018 | Lundi 25 juin 2018 | Oui |
6 | 2018/06/25 | 25 juin 2018 | 25 juin 2018 | Oui |
7 | 2018/06/25 | 2018/06/25 | 2018/06/25 | |
8 | 18/06/25 | 2018/06/25 | 2018/06/25 | Oui |
Extension locale Unicode (ajout de u-ca-japanese)
Locale locale = new Locale("ja", "JP", "JP");
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL).withLocale(locale).format(now); // → (1)
DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG).withLocale(locale).format(now); // → (2)
DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(locale).format(now); // → (3)
DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).withLocale(locale).format(now); // → (4)
Paramètres régionaux par défaut
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL).format(now); // → (5)
DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG).format(now); // → (6)
DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).format(now); // → (7)
DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).format(now); // → (8)
** Résultat de sortie **
pattern | 1.8.0 | 9.0.4 | 10.0.1 | 1.Différence entre 8 et 9 |
---|---|---|---|---|
1 | 25 juin 2018 | Lundi 25 juin 2018 | Lundi 25 juin 2018 | Oui |
2 | 2018/06/25 | 25 juin 2018 | 25 juin 2018 | Oui |
3 | 2018/06/25 | 2018/06/25 | 2018/06/25 | |
4 | 18/06/25 | 2018/06/25 | 2018/06/25 | Oui |
5 | 25 juin 2018 | Lundi 25 juin 2018 | Lundi 25 juin 2018 | Oui |
6 | 2018/06/25 | 25 juin 2018 | 25 juin 2018 | Oui |
7 | 2018/06/25 | 2018/06/25 | 2018/06/25 | |
8 | 18/06/25 | 2018/06/25 | 2018/06/25 | Oui |
DateTimeFormatter.localizedBy
La méthode localiséeBy est une méthode introduite dans Java 10. Si les paramètres régionaux contiennent des extensions Unicode, les paramètres régionaux seront remplacés. (Aucun effet secondaire sur les instances locales)
Locale locale = new Locale("ja", "JP", "JP");
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL).localizedBy(locale).format(now); // → (1)
DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG).localizedBy(locale).format(now); // → (2)
DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).localizedBy(locale).format(now); // → (3)
DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).localizedBy(locale).format(now); // → (4)
** Résultat de sortie **
pattern | 1.8.0 | 9.0.4 | 10.0.1 | 1.Différence entre 8 et 9 |
---|---|---|---|---|
1 | - | - | Mercredi 27 juin 2018 | - |
2 | - | - | 27 juin 2018 | - |
3 | - | - | 27 juin 2018 | - |
4 | - | - | H30/6/27 | - |
Extension locale Unicode (ajout de u-ca-japanese)
Locale locale = new Locale("ja", "JP", "JP");
ZonedDateTime now = ZonedDateTime.now();
DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL, FormatStyle.FULL).withLocale(locale).format(now); // → (1)
DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG, FormatStyle.LONG).withLocale(locale).format(now); // → (2)
DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.MEDIUM).withLocale(locale).format(now); // → (3)
DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT, FormatStyle.SHORT).withLocale(locale).format(now); // → (4)
Paramètres régionaux par défaut
ZonedDateTime now = ZonedDateTime.now();
DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL, FormatStyle.FULL).format(now); // → (5)
DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG, FormatStyle.LONG).format(now); // → (6)
DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.MEDIUM).format(now); // → (7)
DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT, FormatStyle.SHORT).format(now); // → (8)
** Résultat de sortie **
pattern | 1.8.0 | 9.0.4 | 10.0.1 | 1.Différence entre 8 et 9 |
---|---|---|---|---|
1 | 25 juin 2018 22:20:24 JST | Lundi 25 juin 2018 22:22:28 Heure standard japonaise | Lundi 25 juin 2018 22:24:57 Heure standard japonaise | Oui |
2 | 2018/06/25 22:20:24 JST | 25 juin 2018 22:22:28 JST | 25 juin 2018 22:24:57 JST | Oui |
3 | 2018/06/25 22:20:24 | 2018/06/25 22:22:28 | 2018/06/25 22:24:57 | |
4 | 18/06/25 22:20 | 2018/06/25 22:22 | 2018/06/25 22:24 | Oui |
5 | 25 juin 2018 22:20:24 JST | Lundi 25 juin 2018 22:22:28 Heure standard japonaise | Lundi 25 juin 2018 22:24:57 Heure standard japonaise | Oui |
6 | 2018/06/25 22:20:24 JST | 25 juin 2018 22:22:28 JST | 25 juin 2018 22:24:57 JST | Oui |
7 | 2018/06/25 22:20:24 | 2018/06/25 22:22:28 | 2018/06/25 22:24:57 | |
8 | 18/06/25 22:20 | 2018/06/25 22:22 | 2018/06/25 22:24 | Oui |
Il n'y avait aucune différence entre les versions lors de l'exécution d'un modèle.
Locale locale = new Locale("ja", "JP", "JP");
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter.ofPattern("G yyyy-MM-dd (E) a HH:mm:ss.SSS", locale).format(now); // → (1)
DateTimeFormatter.ofPattern("G yyyy-MM-dd (E) a HH:mm:ss.SSS").format(now); // → (2)
DateTimeFormatter.ofPattern("G yy-MM-dd (E) a HH:mm:ss.SSS").localizedBy(locale).format(now); // → (3)
Locale locale = new Locale("ja", "JP", "JP");
ZonedDateTime now = ZonedDateTime.now();
DateTimeFormatter.ofPattern("G yyyy-MM-dd (E) a HH:mm:ss.SSS zzz", locale).format(now); // → (4)
DateTimeFormatter.ofPattern("G yyyy-MM-dd (E) a HH:mm:ss.SSS zzz").format(now); // → (5)
DateTimeFormatter.ofPattern("G yy-MM-dd (E) a HH:mm:ss.SSS zzz").localizedBy(locale).format(now); // → (6)
** Résultat de sortie **
pattern | 1.8.0 | 9.0.4 | 10.0.1 | 1.Différence entre 8 et 9 |
---|---|---|---|---|
1 | AD 2018-06-26 (Feu)00 h 00:01:45.267 | AD 2018-06-26 (Feu)00 h 00:02:50.751 | AD 2018-06-26 (Feu)00 h 00:03:56.584 | |
2 | AD 2018-06-26 (Feu)00 h 00:01:45.267 | AD 2018-06-26 (Feu)00 h 00:02:50.751 | AD 2018-06-26 (Feu)00 h 00:03:56.584 | |
3 | - | - | Heisei 30-06-26 (Feu)00 h 00:26:03.888 | - |
4 | AD 2018-06-26 (Feu)00 h 00:01:45.269 JST | AD 2018-06-26 (Feu)00 h 00:02:50.767 JST | AD 2018-06-26 (Feu)00 h 00:03:56.601 JST | |
5 | AD 2018-06-26 (Feu)00 h 00:01:45.269 JST | AD 2018-06-26 (Feu)00 h 00:02:50.767 JST | AD 2018-06-26 (Feu)00 h 00:03:56.601 JST | |
6 | - | - | Heisei 30-06-26 (Feu)00 h 00:26:03.898 JST | - |
Unicode Technical Standard #35
UNICODE LOCALE DATA MARKUP LANGUAGE (LDML)
Extension de l'internationalisation dans Java SE 6
Il n'y a pas de description sur CLDR. Bien que cela n'ait rien à voir avec CLDR, la prise en charge de l'historique japonais est fournie dans Java SE 6.
** Prise en charge du calendrier japonais **
Une nouvelle implémentation de calendrier a été ajoutée pour prendre en charge le comptage dans le calendrier japonais, comme 2005 (calendrier Gregorio) comme 2005. Cette instance de calendrier japonais peut être créée dans la fabrique Calendar.getInstance en spécifiant Locale ("ja", "JP", "JP"). La classe java.text.SimpleDateFormat prend en charge les formats d'année et de date spécifiques au calendrier autres que le calendrier Gregorio.
Locale locale = new Locale("ja", "JP", "JP");
Calendar calendar = Calendar.getInstance(locale);
System.out.println(calendar.getClass().getCanonicalName());
// → java.util.JapaneseImperialCalendar
System.out.println(new SimpleDateFormat("Gyy année MM mois jj jour(E)", locale).format(calendar.getTime()));
//→ 25 juin 2018(Mois)
Calendar calendar = Calendar.getInstance();
System.out.println(calendar.getClass().getCanonicalName());
// → java.util.GregorianCalendar
System.out.println(new SimpleDateFormat("Gyyyy année MM mois jj jour(E)").format(calendar.getTime()));
//→ 25 juin 2018(Mois)
Extension de l'internationalisation dans Java SE 7
** La classe de localité prend en charge BCP47 et UTR35 **
La classe Locale a été mise à jour pour implémenter des identifiants interchangeables avec BCP 47 (IETF BCP 47 "Tags for Identifying Languages") et LDML pour l'échange de données locales (UTS # 35 "Unicode Locale Data Markup Language"). Prend en charge les extensions de compatibilité BCP 47.
Extension de l'internationalisation dans JDK 8
** Adoption des données Unicode CLDR et des propriétés système java.locale.providers **
Le Consortium Unicode a publié le projet CLDR (Common locale Data Repository) pour «prendre en charge les langues du monde avec le référentiel de données de paramètres régionaux standard le plus grand et le plus complet». CLDR est en train de devenir la norme de facto pour les données locales.
Les données locales basées sur XML de CLDR sont incluses dans la version JDK 8, mais sont désactivées par défaut.
Défaut
Le comportement par défaut est équivalent aux paramètres suivants.
java.locale.providers=JRE,SPI
Extension de l'internationalisation dans JDK 9
** Données locales CLDR activées par défaut **
Les données de paramètres régionaux XML pour le référentiel de données de paramètres régionaux communs Unicode (CLDR) qui ont été ajoutés pour la première fois à JDK 8 sont les données de paramètres régionaux par défaut pour JDK 9. Dans les versions précédentes, la valeur par défaut était JRE.
Défaut
Si vous ne définissez pas cette propriété, le comportement par défaut est équivalent au paramètre suivant:
java.locale.providers=CLDR,COMPAT,SPI
Extension de l'internationalisation dans JDK 10
** Extensions de balises de langage Unicode supplémentaires **
Java SE 9 ne prend en charge que les extensions -ca (calendrier) et -nu (numérique). Java SE 10 ajoute la prise en charge des extensions supplémentaires suivantes dans les classes JDK associées:
- -cu (type de devise)
- -fw (premier jour de la semaine)
- -rg (Remplacement de région)
- -tz (fuseau horaire)
java.time: DateTimeFormatter containing "DD" fails on 3-digit day-of-year value
Java 9 a résolu le problème selon lequel une exception se produit lorsque "DD" est spécifié dans la chaîne de modèle lorsque le nombre total de jours dans la date cible est de 100 jours ou plus.
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter.ofPattern("D").format(now);
// → 177
DateTimeFormatter.ofPattern("DD").format(now); // ← Java 1.Exception dans 8
// → 177
DateTimeFormatter.ofPattern("DDD").format(now);
// → 177
DateTimeFormatter won't parse dates with custom format "yyyyMMddHHmmssSSS"
Dans Java 1.8, le bogue selon lequel une exception se produit lorsque "aaaaMMjjHHmmssSSS" est spécifié dans la chaîne de modèle a été résolu dans Java 9.
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS").format(now);
// → 20180625222024147
java.time.format.FormatStyle.LONG or FULL causes unchecked exception
Le formatage de LocalDateTime avec DateTimeFormatter.ofLocalizedDateTime (FormatStyle.FULL) soulève une exception, mais c'est par conception et pas un problème.
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL).format(now);
// → Exception in thread "main" java.time.DateTimeException: Unable to extract ZoneId from temporal 2018-06-26T01:00:54.557262700
Dans le cas de ZonedDateTime, il peut être formaté sans aucun problème.
ZonedDateTime now = ZonedDateTime.now();
DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL, FormatStyle.FULL).format(now);
//→ lundi 25 juin 2018 22:24:57 heure standard japonaise
Correspondance pour d'autres chaînes de caractères de modèle (correspondance conforme aux spécifications CLDR plutôt qu'à l'addition)
DateTimeFormatter pattern letters 'A','n','N' Add date-time patterns 'v' and 'vvvv' DateTimeFormatter pattern letter 'g' Incorrect documentation for DateTimeFormatter letter 'k'
Je saisis ce qui m'intéresse, donc tous ne sont pas listés ici.
Japanese new era implementation Release Note: Japanese New Era Implementation
Ceci est une réponse à la révision qui aura lieu le 1er mai 2019. Pour le moment, "New Era" sera affiché dans l'édition originale en tant que mesure provisoire.
Release Note: Update locale data to Unicode CLDR v33
Données CLDR vers la version 33 seront mises à jour.
Recommended Posts