Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

Intlの今までとこれから

Saji
November 18, 2023

 Intlの今までとこれから

JSConf JP 2023 : 2023/11/19
- https://jsconf.jp/2023/talk/ryusei-sajiki-1/

Saji

November 18, 2023
Tweet

More Decks by Saji

Other Decks in Technology

Transcript

  1. 8IBUJT*OUM࢓༷ॻʹΑΔͱ This Standard de fi nes the application programming interface

    for ECMAScript objects that support programs that need to adapt to the linguistic and cultural conventions used by different human languages and countries.(from ECMA-402, 10th edition) ͜ͷඪ४͸ɺҟͳΔਓؒͷݴޠ΍ࠃͰ࢖༻͞ΕΔݴޠతɾจԽతͳ׳शʹదԠ ͢Δඞཁͷ͋ΔϓϩάϥϜΛαϙʔτ͢ΔECMAScriptΦϒδΣΫτͷΞϓϦέ ʔγϣϯϓϩάϥϛϯάΠϯλϑΣʔεΛఆٛ͢Δɻ
  2. *OUMͷ"1*֓ཁ શͯͷίϯετϥΫλϓϩύςΟ͸MPDBMFͱ0QUJPOͷ̎ͭΛҾ਺ʹऔΔ w -PDBMF࢖༻͢ΔMPDBMFɻݴޠλά͔-PDBMFΦϒδΣΫτͰࢦఆɻ w ྫ“ja-JP”, “en-US” w 0QUJPOGPSNBUͳͲʹ͓͚ΔΦϓγϣϯɻ w

    ྫ{calender : "japanese"} ࢖༻͢ΔྐྵΛ࿨ྐྵʹ͢Δ  ίϯετϥΫλΛॳظԽ͢ΔࡍʹϩέʔϧͱϑΥʔϚοτͳͲͷΦϓγϣϯΛࢦ ఆ͢Δͷ͕ϙΠϯτ
  3. ࢖͍ํͷྫ const formatter = new Intl.NumberFormat("en", { notation: "compact", compactDisplay:

    "long", }); formatter.format(123456789); ίϯετϥΫλϓϩύςΟΛOFX
  4. ࢖͍ํͷྫ const formatter = new Intl.NumberFormat("en", { notation: "compact", compactDisplay:

    "long", }); formatter.format(123456789); ࣮ࡍʹGPSNBUϝιουΛݺͼग़͢
  5. ࢖͍ํͷྫ const formatter = new Intl.NumberFormat("en", { notation: "compact", compactDisplay:

    "long", }); formatter.format(123456789); // → 123 million ϑΥʔϚοτ͞ΕΔʂ
  6. *OUM%BUF5JNF'PSNBUͷ࢖༻ྫ const formatter = Intl.DateTimeFormat('en-US', { dateStyle: 'short' }); const

    date = new Date(Date.UTC(2023, 10, 19)); formatter.format(date); // → 11/19/23 formatter.formatToParts(date); // [ // { type: 'month', value: '11' }, // { type: 'literal', value: '/' }, // { type: 'day', value: ’19' }, // { type: 'literal', value: '/' }, // { type: 'year', value: '23' } // ];
  7. Α͘࢖͏ػೳ*OUM/VNCFS'PSNBU ७ਮͳ਺஋͚ͩͰͳ͘୯Ґ΍௨՟ɺύʔηϯτΛؚΜͩ਺஋ͷϑΥʔϚοτ w “decimal”ྫ “123.4” w “currency”ྫ “$123.4” w “percent”ྫ

    “123.4%” w “unit”ྫ “123.4km/h” ϝιου͸%BUF5JNF'PSNBUͱಉ༷ w format()/formatRange()/formatToParts()/formatRangeToParts()
  8. *OUM/VNCFS'PSNBUͷ࢖༻ྫ ਺஋ const compactFormatter = new Intl.NumberFormat('en', { style: 'decimal',

    notation: 'compact' }); compactFormatter.format(1000000); // '1M' const FourDigitFormatter = new Intl.NumberFormat('en', { style: 'decimal', maximumSignificantDigits: 4 }); FourDigitFormatter.format(123.456); // "123.5"
  9. *OUM/VNCFS'PSNBUͷ࢖༻ྫ ௨՟ const enFormatter = new Intl.NumberFormat("en", { style: "currency",

    currency: "USD", }); enFormatter.format(12345); // “$12,345.00” const jaFormatter = new Intl.NumberFormat("ja", { style: “currency", currency: "JPY", }); jaFormatter.format(12345); // “ˇ12,345”
  10. *OUM/VNCFS'PSNBUͷ࢖༻ྫ ୯Ґ const formatter = new Intl.NumberFormat("en", { style: "unit",

    unit: "foot", unitDisplay: "long", }); formatter.format(1); // → 1 foot formatter.format(2); // → 2 feet
  11. ೥ TU&EJUJPOࡦఆ͔Β೥ $ISPNF *& 'JSFGPY 4BGBSJ %BUF5JNF'PSNBU ⚪︎ ˚ º

    º /VNCFS'PSNBU ⚪︎ ˚ º º $PMMBUPS ⚪︎ ˚ º º
  12. ೥ $ISPNF *& 'JSFGPY 4BGBSJ %BUF5JNF'PSNBU ⚪︎ ˚ ⚪︎ º

    /VNCFS'PSNBU ⚪︎ ˚ ⚪︎ º $PMMBUPS ⚪︎ ˚ ⚪︎ º
  13. ೥ $ISPNF *&ˠ&EHF 'JSFGPY 4BGBSJ %BUF5JNF'PSNBU ⚪︎ ⚪︎ ⚪︎ º

    /VNCFS'PSNBU ⚪︎ ⚪︎ ⚪︎ º $PMMBUPS ⚪︎ ⚪︎ ⚪︎ º
  14. ೥ $ISPNF *&ˠ&EHF 'JSFGPY 4BGBSJ %BUF5JNF'PSNBU ⚪︎ ⚪︎ ⚪︎ ⚪︎

    /VNCFS'PSNBU ⚪︎ ⚪︎ ⚪︎ ⚪︎ $PMMBUPS ⚪︎ ⚪︎ ⚪︎ ⚪︎
  15. UI UI&EJUJPO   formatToParts() (DateTimeFormat / NumberFormat) // ҙຯ͝ͱʹformatจࣈྻΛ෼ׂͨ͠഑ྻͱͯ͠ฦ͢

    const formatter = Intl.DateTimeFormat('en-US', {}); formatter.formatToParts(new Date(Date.UTC(2023, 10, 19))); // [ // { type: 'month', value: '11' }, // { type: 'literal', value: '/' }, // { type: 'day', value: ’19' }, // { type: 'literal', value: '/' }, // { type: 'year', value: '23' } // ];
  16. UI&EJUJPO  Intl.PluralRules // ং਺΍ෳ਺ܗͷϧʔϧΛ൑ผ͢Δػೳͷ௥Ճ const pluralRule = new Intl.PluralRules("en-US",

    { type: "ordinal", }); pluralRule.select(1); // → "one" : 1st pluralRule.select(2); // → "two" : 2nd pluralRule.select(3); // → "few" : 3rd pluralRule.select(13); // → "other" : 13th pluralRule.select(103); // → "few" : 103rd
  17. UI&EJUJPO  Intl.RelativeTimeFormat // ʮࡢ೔ʯͷΑ͏ͳ૬ରతͳ೔࣌දهͷϑΥʔϚοτ const rtfEn = new Intl.RelativeTimeFormat("en",

    { numeric: "auto", }); rtfEn.format(1, "day"); // "tomorrow" const rtfJa = new Intl.RelativeTimeFormat("ja", { numeric: "auto", }); rtfJa.format(2, "day"); // "໌ޙ೔"
  18. UI&EJUJPO  Intl.Locale Object // ϩέʔϧ৘ใΛΑΓ؆୯ʹૢ࡞ɾ؅ཧͰ͖Δ const korean = new

    Intl.Locale("ko", { script: "Kore", region: "KR", hourCycle: "h23", calendar: "gregory", }); console.log(korean.hourCycle); // "h23"
  19. UI&EJUJPO  Intl.ListFormat // ݴޠΛߟྀͨ͠Ϧετ(and/orͰͲ͏ܨ͙͔)ͷϑΥʔϚοτ const languages = ['JavaScript', 'JavaScript',

    'HTML']; const formatter = new Intl.ListFormat('en', { style: 'long', type: 'conjunction', }); console.log(formatter.format(languages)); // "JavaScript, JavaScript, and HTML"
  20. UI&EJUJPO  Intl.DisplayNames // ݴޠ໊ɺ஍Ҭ໊ɺจࣈମܥ໊ࣗମͷදࣔΛࠃࡍԽ const regionNamesInJapanese = new Intl.DisplayNames(

    ["ja"], { type: "language" }, ); regionNamesInJapanese.of("en"); // “ӳޠ” regionNamesInJapanese.of("ja"); // “೔ຊޠ"
  21. UI&EJUJPO  DateTimeFormat:formatRange() // ೔࣌ͷظؒදهͷϑΥʔϚοτΛߦ͏ const formatter = Intl.DateTimeFormat('en-US', {

    dateStyle: 'short' }); const dateStart = new Date(Date.UTC(2023, 10, 18)); const dateEnd = new Date(Date.UTC(2023, 10, 19)); formatter.formatRange(dateStart, dateEnd); // '11/18/23 – 11/19/23'
  22. UI&EJUJPO  'dayStyle','timeStyle' in DateTimeFormat // ҙຯ͝ͱʹformatจࣈྻΛ෼ׂͨ͠഑ྻͱͯ͠ฦ͢ const dateFormatter =

    Intl.DateTimeFormat("en-US", { dateStyle: "short", }); const timeFormatter = Intl.DateTimeFormat("en-US", { timeStyle: "long", }); const date = new Date(Date.UTC(2023, 10, 19)) dateFormatter.format(date); // “11/19/23" timeFormatter.format(date); // "9:00:00 AM GMT+9"
  23. UI&EJUJPO  Intl.Segmenter // จࣈɾ୯ޠɾจ͝ͱͷ෼ͪॻ͖Λߦ͏ const segmenter = new Intl.Segmenter("ja-JP",

    { granularity: "word" }, ); console.log([...segmenter.segment("ޗഐ͸ೣͰ͋Δɻ")]); // [ // {"segment": "ޗഐ", "index": 0, "isWordLike": true}, // {"segment": "͸", "index": 2, "isWordLike": true}, // ...(ུ)
  24. UI&EJUJPO  Intl.DisplayNames v2 // ΧϨϯμʔ໊ɺ೔࣌ͷϥϕϧ໊ࣗମͳͲͷදࣔΛࠃࡍԽ const calendarNamesInJapanese = new

    Intl.DisplayNames( ["ja"], { type: "calendar" }, ); calendarNamesInJapanese.of("japanese"); // “࿨ྐྵ” const dateTimeNamesInJapanese = new Intl.DisplayNames( ["ja"], { type: "dateTimeField" }, ); dateTimeNamesInJapanese.of("dayPeriod"); // “ޕલ/ޕޙ”
  25. UI&EJUJPO  Intl Enumeration API // ͦͷ࣮૷Ͱαϙʔτ͞Ε͍ͯΔྐྵɺর߹ॱংɺͳͲͷ഑ྻΛฦ͢ Intl.supportedValuesOf("calendar"); // ['buddhist',

    'chinese', 'coptic', ‘dangi’, …]; Intl.supportedValuesOf("currency"); // [‘AED', 'AFN', 'ALL', 'AMD', 'ANG', 'AOA', ‘ARS’, …] Intl.supportedValuesOf("numberingSystem"); // [‘adlm', 'ahom', 'arab', 'arabext', 'bali', …] Intl.supportedValuesOf("timeZone"); // ['Africa/Abidjan', 'Africa/Accra', …]
  26. UI&EJUJPO  Intl.NumberFormat v3 // formatRange()ϝιουͷαϙʔτ const formatter = new

    Intl.NumberFormat('en', { style: 'decimal', }); formatter.formatRange(100, 200); // ‘100-200’ // ਺஋ͷؙΊΦϓγϣϯͳͲͷॆ࣮ const compactFormatter = new Intl.NumberFormat('en', { style: ‘decimal', maximumFractionDigits: 0 }); compactFormatter.formatR(3.14); // ‘3’
  27. TU&EJUJPOd·Ͱͷػೳ௥Ճ + Intl.getCanonicalLocales() + Intl.ListFormat + Intl.Segmenter + Intl.DisplayNames +

    Intl.PluralRules + Intl.RelativeTimeFormat + Intl.Locale + formatToParts() + formatRange() + DateTimeFormat option + NumberFormat option(v3) … and more
  28. ݱࡏͷαϙʔτঢ়گ $ISPNF &EHF 'JSFGPY 4BGBSJ %BUF5JNF'PSNBU    

    /VNCFS'PSNBU     4FHNFOUFS   º  1MVSBM3VMFT     3FMBUJWF5JNF'PSNBU    
  29. ݱࡏͷαϙʔτঢ়گ $ISPNF &EHF 'JSFGPY 4BGBSJ 1MVSBM3VMFT    

    -PDBMF0CKFDU     -JTU'PSNBU     %JTQMBZ/BNFT    
  30. ؔ࿈࢓༷Λ஌Δҙຯ &DNB4DSJQUಠࣗͰܾΊΔΘ͚ʹ͸ߦ͚ͳͦ͞͏ͳ໰୊͕୔ࢁ w αϙʔτ͢Δจࣈ΍ͦͷιʔτ͸ʁ w αϙʔτ͢ΔλΠϜκʔϯ͸  w ݴޠʹΑΔจ๏ࣄ߲ ෳ਺ܗͳͲʜ

    ͷ෼ྨͬͯʁ w ஍Ҭ΍ݴޠ໊Ͳ͏΍ͬͯ؅ཧ͢Δʁ ˠ*OUM͸ࠃࡍԽػೳͱ͍͏ੑ্࣭ɺଞͷඪ४࢓༷Λࢀর͢Δ෦෼͕ଟ͍
  31. *OUM-PDBMF*OGP"1* 4UBHF ϩέʔϧ৘ใΛอ࣋͢ΔIntl.LocaleΦϒδΣΫτʹର༷ͯ͠ʑͳσʔλΛऔ ಘͰ͖ΔϝιουΛ௥Ճ͢ΔఏҊ w ྫ ʮจࣈΛॻ͘ํ޲ʯ΍ʮि࢝·Γʹ͍ͭͯͷ৘ใʯͳͲ const enGB =

    new Intl.Locale('en-GB'); enGB.getWeekInfo(); // {firstDay: 1, weekend: [6, 7], minimalDays: 4} let tl = enGB.getTextInfo(); // { direction: "rtl" }
  32. *OUM.FTTBHF'PSNBUྫ const source = ... // ্ͷϑΥʔϚοτจࣈྻ const mf =

    new Intl.MessageFormat(source, 'en'); const notifications = mf.format({ count: 1 }); // 'You have 1 new notification' match {$count :number} when 0 {You have no new notifications} when one {You have {$count} new notification} when * {You have {$count} new notifications} *$6ͷϑΥʔϚοτจࣈྻ .FTTBHF'PSNBUW