À propos des données locales CLDR activées par défaut à partir de Java 9

Aperçu

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

Différences de comportement entre les versions Java

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

À propos des paramètres régionaux ("ja", "JP", "JP") des paramètres régionaux spéciaux

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:

Remplacer le type de devise des paramètres régionaux

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

Remplacer le premier jour de la semaine dans le calendrier

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)

Date et DateFormat

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

Calendrier et dateFormat

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

LocalDateTime et DateTimeFormatter

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 -

DateHeure et DateTimeFormatter zonées

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

Spécifiez n'importe quel motif

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 -

Supplément

Unicode Technical Standard #35

UNICODE LOCALE DATA MARKUP LANGUAGE (LDML)

Transition des fonctions étendues d'internationalisation

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)

Problèmes résolus dans Java 9

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'

Problèmes résolus par Java 11

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

À propos des données locales CLDR activées par défaut à partir de Java 9
Appelez l'API Microsoft Emotion en envoyant des données d'image directement depuis Java.
[Java Siler] À propos de l'inférence de type par var
Traitement des données à l'aide de l'API de flux de Java 8
Utiliser le type de données PostgreSQL (jsonb) à partir de Java
Flux de données Kinesis à partir de zéro expérience Java (3.1)
Flux de données Kinesis à partir de zéro expérience Java (3.2)
À propos des types de données Java (en particulier des types primitifs) et des littéraux
Mémo: [Java] Obtenez des données Raspberry Pi avec SFTP