[OUTDATED] [2.5.1] International - Make i18n easier, from number formatting to plurals

(NOTE: This library is out of date, I’m working on version 3 which is a complete rewrite and a huge improvement)

International (or Intl), is a module for simple language-sensitive number formatting, date & time formatting, relative time formatting and plurals rule.

Where to get it?

From GitHub, or from here:

2.5.1
File (9.5 MiB)

2.5
File (9.5 MiB)

2.4
File (7.9 MiB) (All locale data version.rbxm (9.5 MiB))

2.3.1
File (7.8 MiB) (All locale data version (9.4 MB))

2.3
File (7.7 MiB) (All locale data version (9.3 MiB))

Features

toLocaleString

A formatter that directly formats numbers, dates, lists without creating its respective format. If the value is determined to be numeric (number or BigNum or BigInt) it’ll directly format the number, if the value is determined to be a date, it’ll directly format the date, if the value is determined to be a list, it’ll directly format the list.
Edit: For 2.2, Lists creates Intl.ListFormat, I was incorrect, it only directly format numbers and dates

Before 2.2

A formatter that supports date formatting, number formatting, list formatting. This is a sugar syntax, for Intl.NumberFormat.new(locale, options):Format(value) if the value is determined to be numeric, Intl.DateFormat.new(locale, options):Format(value), if the value is determined to be a date, and Intl.ListFormat.new(locale, options):Format(value) if the value is determined to be a list.

Locale - International.Locale

A powerful locale class, all input are valid as long it’s a valid IETF BCP 47 tag (no -t- extension, and -x- extension are ignored but are still required to be valid).
It supports the -u- extension.

Number formatting - International.NumberFormat

A powerful language sensitive number formatting, even only with the en locale, it has:

  • The ability to zero pad numbers (minimumIntegerDigits)
  • Round to certain dedimal places (minimumFractionDigits, maximumFractionDigits)
  • Round to significant digits (minimumSignificantDigits and maximumSignificantDigits)
  • Currency formatting (style = "currency"), input any valid currency code as the currency option (e.g USD) and it’ll do it right away
  • Percent formatting (style = "percent") considered too
  • Number abbreviation/shortening/compact number (notation = "compact"), oh you can customise the decimal place with (minimumFractionDigits, maximumFractionDigits, minimumSignificantDigits and maximumSignificantDigits) This can only go up to a trillion, so 1 quadrillion will return 1000T
  • Not just number abbreivation, long compact numbers too (compactDisplay = "long") so thousand instead of K
  • Scientific notation support too.
  • Formatting it to parts for more advanced programmer, so you can it make it look like 1,000.50 or 1.2K with enough knowledge, oh it’s laid out in the similar manner to ECMA 402 (and globalize.js) number part formatting, so if you’re used to ECMA 402, no problem
{
    { type = "integer", value = "1"},
    { type = "group", value = ","},
    { type = "integer", value = "234"},
    { type = "decimal", value = "."},
    { type = "fraction", value = "56"}
}
  • Negaitve number and decimal support
  • Oh, not just that, it also support infinity and nan, and that is language sensitive too.
intl.NumberFormat.new('en'):Format(math.huge) --> ∞
intl.NumberFormat.new('en'):Format(0/0) --> NaN
intl.NumberFormat.new('ar'):Format(0/0) --> ليس رقمًا
  • Unit support too (style = "unit"), with units like inches (unit = "inch"), metres (unit = "meter"), kilograms (unit = "kilogram"), celsius (unit = "celsius"), fahrenehit (unit = "fanreheit") seconds (unit = "second"), etc.
  • More numbering system, it’s not just 123456789, but ١٢٣٤٥٦٧٨٩, 一二三四五六七八九, ௧௨௩௪௫௬௭௮௯, ၁၂၃၄၅၆၇၈၉, etc.
-- With -u- extension and the numberingSystem option, this isn't specific to locale :)
intl.NumberFormat.new('en-u-nu-arab'):Format(12345) --> ١٢,٣٤٥
  • The ability to let you choose to group the digit or not, (for versions before 2.1, true to enable grouping and false to disable grouping, and it defaults to true) for version 2.1 and over, you can choose the following:
    • never to not group the digit
    • auto for determining what number to group depending on the locale (default expect for numeral abbreviation/compact numbers)
    • min2 to not group 1000 but group 10 000 (if the grouping size is 3), bascially set the minimum grouping digit to 2 (regardless of the locale’s minimum grouping digit value for versions < 2.4), if it’s below 2 (for versions ≥ 2.4). (default for numeral abbreviation/compact numbers)
    • always to group the digit regardless of the locale’s minimum grouping digit value.
    • thousands to group the digit by thousands (minimum grouping digits aren’t accounted, only available on 2.4 and over)
  • Range formatting (for 2.2 and over) with :FormatRange()
  • Infinite precision (If BigDecimal or a numeric string inputted)
  • BigNum/BigInteger support (Yes BigNum/BigInteger works here unlike most number formatting modules)
  • In fact a numeric string support, so it’s not limited.
print(intl.NumberFormat.new('en'):Format('9007199254740993')) --> 9,007,199,254,740,993
-- This module is capable of this :)
print(intl.NumberFormat.new('en'):Format(BigInteger.new(10) ^ 1000)) --> 10,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000
-- Don't forget it's locale aware
print(intl.NumberFormat.new('de'):Format(BigInteger.new(10) ^ 1000)) --> 10.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000

Date formatting - International.DateTimeFormat

A powerful yet flexibile language-sensitive date formatting. It finds the closest pattern and with the so-called smart date format negotiation determined by what value options is inputted.
If no/empty table option is included it defaults to { dateStyle = 'medium' } (keep in mind depending on the options, many date parts might be ignored)

print(intl.DateTimeFormat.new('en'):Format{ year = 2012, month = 3, day = 4, hour = 12, min = 34 }) --> Mar 4, 2012 12:34:00 PM
print(intl.DateTimeFormat.new('en', { dateStyle = "medium" }):Format{ year = 2012, month = 3, day = 4 }) --> Mar 4, 2012
print(intl.DateTimeFormat.new('en', { dateStyle = "full" }):Format{ year = 2012, month = 3, day = 4 }) --> Sunday, March 4, 2012
print(intl.DateTimeFormat.new('en', { month = "short", day = "numeric" }):Format{ year = 2012, month = 3, day = 4 }) --> Mar 4
print(intl.DateTimeFormat.new('en', { hour = "numeric", minute = "2-digit" }):Format{ year = 2012, month = 3, day = 4, hour = 13, minute = 45 }) --> 1:45 PM
-- 12 and 24 hours isn't tied by locale, with -u- extension and the hourCycle and hour12 option :)
print(intl.DateTimeFormat.new('en', { hour12 = false, hour = "numeric", minute = "2-digit" }):Format{ year = 2012, month = 3, day = 4, hour = 13, minute = 45 }) --> 13:45
print(intl.DateTimeFormat.new('en-u-hc-h23', { hour = "numeric", minute = "2-digit" }):Format{ year = 2012, month = 3, day = 4, hour = 13, minute = 45 }) --> 13:45

It doesn’t only support gregorian, it supports the following calendar:

  • Islamic (might be one day off)
  • Japanese
  • Republic of China
  • Buddhist
  • Chinese (only from 1901 to 2100, added in 2.4)

Relative time formatting - International.RelativeTimeFormat

Language sensitive relative time formatting, positive values (and 0) indicates future and negative values indicates past, this doesn’t accept NaN but it does accept Infinity, it supports the following:

  • years
  • quarters
  • months
  • mondays to sundays
  • week
  • days
  • hour
  • minute
  • second

(Unlike intl.NumberFormat, this doesn’t accept BigNum/BigInt, and only accepts number values that’s not NaN or strings that can be converted to number)

local formatter = intl.RelativeTimeFormat.new('en', { numeric = "auto" })
print(formatter:Format(-2, 'day')) --> 2 days ago
print(formatter:Format(-1, 'day')) --> yesterday
print(formatter:Format(0, 'day')) --> today
print(formatter:Format(1, 'day')) --> tomorrow
print(formatter:Format(2, 'day')) --> in 2 days
-- Inserting NaN will throw an error
print(formatter:Format(tonumber('nan'), 'day')) --> Value must not be NaN

Plural rule - International.PluralRules

A language-sensitive plural rule handling, can only return the following:

  • zero
  • one
  • two
  • few
  • many
  • other
    In English, only one and other can be returned for cardinal, one for singular, other for plural.
local englishPlural = intl.PluralRules.new('en');
print(englishPlural:Select(1)) --> one
print(englishPlural:Select(2)) --> other

It also supports ordinals with type = "ordinal" option, one for st, two for nd, few for rd and other for th.

Locale display names - International.DisplayNames

Maybe there’s a case where you want to display locale names in whatever language you want? Territory names? Script names? With International, all you need to do is create a DisplayName then run the :Of method, that’s it, regional/script/variant part in locales are dealt with.

local englishDisplayName = Intl.DisplayNames.new('en');
print(englishDisplayName:Of('en')) --> English
print(englishDisplayName:Of('en-US')) --> American English
print(englishDisplayName:Of('en-GB')) --> British English
print(englishDisplayName:Of('de')) --> German
print(englishDisplayName:Of('ja')) --> Japanese
print(englishDisplayName:Of('eo')) --> Espernato

local shortEnglishDisplayName = Intl.DisplayNames.new('en', { style = "short"  });
print(shortEnglishDisplayName:Of('en-US')) --> US English
print(shortEnglishDisplayName:Of('en-GB')) --> UK English

local englishTerritoryName = Intl.DisplayNames.new('en', { type = "region" });
print(englishTerritoryName:Of('DE')) --> Germany
print(englishTerritoryName:Of('JP')) --> Japan
print(englishTerritoryName:Of('ES')) --> Spain
print(englishTerritoryName:Of('419')) --> Latin America
print(englishTerritoryName:Of('KP')) --> North Korea
print(englishTerritoryName:Of('UN')) --> United Nations
print(englishTerritoryName:Of('EU')) --> European Union
print(englishTerritoryName:Of('001')) --> World

Segmenter - International.Segmenter

Only available on version 2.3 and over
Maybe you want to split text? words? sentences? No problem, with this capable of splitting words, graphemes and sentences.

local segmenter = International.Segmenter.new('en', { granularity = "word" })
for index, segment in segmenter:Segment("hello world") do
    print(index, segment)
end;

Outputs

1 hello
6  
7 world

If you prefer table over iterator you can use Segmenter:Split(text).
With sentence suppressions depending on the locale. (Via the suppression option)

List formatting - International.ListFormat

Have you ever wanted this

{'apple', 'pears', 'oragnes'}

to become this?

apple, pears, and oranges

Sure, this is a language-sensitive list formatter, just create a ListFormat then run :Format, the most basic usage are something like

local list = {'a', 'b', 'c'}
local listFormatter = Internatoinal.ListFormat.new()
print(listFormatter:Format{'apple', 'pears', 'oranges'}) --> apple, pears, and oranges
print(listFormatter:Format(list)) --> a, b, and c

There’s also :FormatToParts method.

It also supports or-styled and unit lists with the type option, conjunction for and-styled, disjunction for or-styled and unit for unit, and in the style option there’s long, short, and narrow.

Summary

  • Complex Locale matcher, and better Locale system.
  • Language-sensitive number formatting with an options to round to decimal placss and significant figures, with compact numbers (both short and long) with BigNum/BigInt support
  • Language-senstiive date formatting with options.
  • Plural rule handling
  • Locale display name
  • Language sensitive list formatting
31 Likes

Hi This Looks Cool But The Post Is Very Large And Messy. I Recommend Moving The Documentation To GitHub Pages. Kampfkarren Does This For DataStore2 And It Looks Good. I Recommend You Try It.

2.0.0 update, I’ve revamped many parts of the module. For anyone using Version 1, here:

Version 1 post

International, a module for i18n. hope you may find this useful.
Preceded by my module, FormatNumber and CLDR.

FormatNumber
             → International
CLDRTools (obsolete)

What’s this?

This is an international module that’s capable of the following in many langauges:

  • Formatting/abbreviating numbers
  • Formatting dates (gregorian calendar only for versions ≤1.0.0a2, only the Gregorian, Republic of China, Japanese and Buddhist for the current version)
  • Formatting lists
  • Getting plurals
  • Getting locale display names

This module takes a lot of space, and is not completely finished.

If you come from FormatNumber module and CLDRTools:

i18n wise:

  • This has been replaced by International Module
  • I suggest you move to this module if you can.

If you’re not doing i18n:

  • I wouldn’t recommend moving to this module, this and even FormatNumber module is overkill and more than enough for this, as this module is heavy

Some question answered

Where did you get all this data from?
Unicode CLDR. I got the json version, you can get the xml version from here too.

Why doesn’t 1000 group on Spanish locale but 10.000 groups?
This has been asked before.
The Minimum Grouping Digits has been set to 2 in the Spanish locale at least according to CLDR Survey Tool.
This can help: http://cldr.unicode.org/translation/-core-data/numbering-systems

Can you make this module group 1000 for Spanish locale?
Sorry, but no. If you are really that desperate, you might want to ask Unicode CLDR to change this, or use my previous module.

Why doesn’t German group 1000 for compact numbers?
Compact numbers (number abbreviation) override Minimum Grouping Digits to 2 if it’s lower than 2, regardless of locale.

Where to get it?

You can get it from GitHub (1.0.0b2)
1.0.0b2: International1.0.0b2.zip (8·3 MiB)
1.0.0b2 (Lighest, only has the en_US, en and root locale): InternationalLightest.rbxm (212·8 KiB)
1.0.0b: International1.0.0b.zip (8.3 MiB)
1.0.0a: International1.0.0a.zip (7·8 MiB)

You are free to remove all (except the root and anything from the core data) the locale data if you want to, that’s something I’d actually kinda encourage as this moudle takes a lot of space :wink:

API

string intl.ToLocaleString(object value, Locale locale, table options) (intl.tolocalestring for versions ≤1.0.0b)
Formats the value depending on the type and locale

-- If the locale argument is nil, it'll get the locale automatically
print(intl.ToLocaleString(1234.56) --> The number will depend on locale

-- The number format most commonly used in English
print(intl.ToLocaleString(1234.56, 'en_US')) --> 1,234.56

-- Germans use full stops and commas the same as we use commas and full stops
print(intl.ToLocaleString(1234.56, 'de')) --> 1.234,56

-- French uses spaces
print(intl.toLocaleString(1234.56, 'fr')) --> 1 234,56

-- Indian English groups digits differently
print(intl.ToLocaleString(1234567, 'en_IN')) --> 12,34,567

-- We know it's the Eastern Arabic numeral
print(intl.toLocaleString(1234.56, 'ar')) --> ١٬٢٣٤٫٥٦

-- We know Burmese numerals too
print(intl.ToLocaleString(1234.56, 'my')) --> ၁,၂၃၄.၅၆

-- Abbreviating numbers too
print(intl.ToLocaleString(12345, 'en', { notation = "compact" })) --> 12K
print(intl.ToLocaleString(1234567, 'de', { notation = "compact" })) --> 1,2 Mio.
-- We can handle this too
print(intl.ToLocaleString(12345, 'ja', { notation = "compact" })) --> 1.2万
-- Prefixes? No problem
print(intl.ToLocaleString(1234, 'sw', { notation = "compact" }) --> elfu 1.2

-- This is normal behaviour!
-- In the es locale, the minimum grouping digits are set to 2!
print(intl.ToLocaleString(1234, 'es')) --> 1234
print(intl.ToLocaleString(12345, 'es')) --> 12.345

-- American English uses the month > day < year order
print(intl.ToLocaleString({ year = 2012, month = 3, day = 4 }, 'en_US')) --> Mar 4, 2012, 12:00:00 AM

-- With the _u_ extension, the hour cycle doesn't have to be tied by the locale
print(intl.ToLocaleString({ year = 2012, month = 3, day = 4 }, 'en_US_u_hc_h23')) --> Mar 4, 2012, 00:00:00

-- British English uses the day < month < year order
print(intl.ToLocaleString({ year = 2012, month = 3, day = 4 }, 'en_GB')) --> 4 Mar 2012, 00:00:00

-- Japanese uses the year > month > day order
print(intl.ToLocaleString({ year = 2012, month = 3, day = 4 }, 'ja')) --> 2012/03/04 0:00:00

-- It doesn't have to be gregorian.
print(intl.ToLocaleString({ year = 2012, month = 3, day = 4 }, 'ja_JP_u_ca_japanese')) --> 平成24年3月4日 0:00:00

-- We supports Buddhist calendar too.
print(intl.ToLocaleString({ year = 2012, month = 3, day = 4 }, 'th_TH'))) --> 4 มี.ค. 2555 00:00:00

string/nil intl.GetType(object intldata)
Gets the type string of the intldata, if it’s not an intldata, it’ll return nil.

print(intl.GetType(NumberFormat.new())) --> NumberFormat
print(intl.GetType(Locale.new('en'))) --> Locale
print(intl.GetType('not an intldata')) --> nil

table intl.GetData(Locale locale, table/Folder/ModuleScript data, bool inherit = true)
Get data of the locales, the key value of the table must be an locale identifier in strings.
If you don’t want the locale parent’s data but want the locale data, there’s an optional inherit argument, you can set to false.

string intl.TestFormat(Locale locale)
Number & date format example

Locale

Locale intl.Locale.new(string language, string script = nil, string region = nil, string variant = nil, table u_extension = { })
Locale intl.Locale.new(table options)
Creates a Locale, out of the paramters. If the language parameter contain _ or -, it’ll be parsed

print(Locale.new('en', 'US')) --> Locale English (United States): en_US
print(Locale.new('zh', 'Hant', 'TW')) --> Locale Chinese (Traditional, Taiwan): zh_Hant_TW
print(Locale.new('de_AT')) --> Locale German (Austria): de_AT

Locale intl.Locale.fromIdentifier(string identifier)
Creates a Locale based on the identifier.

Locale/nil intl.Locale.negotiate(table/list/tuple preferred, table/list/tuple available = all locales)
Finds the best match between available and preferred locale

Locale intl.Locale.root
Returns the root locale

Locale intl.Locale.RobloxLocale
Returns the locale set on Roblox

Locale intl.Locale.SystemLocale
Returns the locale set on the System (not accurate as it uses Roblox’s localisation service)

Locale intl.Locale.GetLocale()
Get the locale

Locale intl.Locale.SetLocale(Locale locale)
Set the locale, so that it’ll automatically fallback to that locale if the locale value is nil

Properties and class methods

string Locale.language
Gets the language of the locale

string Locale.script
Gets the script of the locale

string Locale.region
Gets the region of the locale

string Locale.variant
Gets the variant of the locale

string Locale.u_extension
Gets the u extension of the locale

string Locale.Name
Gets the base name of the locale (no u_extension)

string Locale.uName
Gets the locale name including the u extension

string Locale.EnglishName
Gets the English name of the locale

string Locale.NativeName
Gets the native name of the locale

string Locale.CharacterOrder
The character order of the locale it can return either left-to-right, right-to-left, top-to-bottom or bottom-to-top

string Locale.LineOrder
The line order of the locale, it can return either left-to-right, right-to-left, top-to-bottom or bottom-to-top

table Locale.MeasurementSystemNames
The measurement system names of the locale

Locale Locale.Parent
Gets the parent of the locale

print(Locale.new('de_DE').Parent) --> Locale German: de
print(Locale.new('es_MX').Parent) --> Locale Spanish (Latin America): es_419
-- Roots don't have a parent!
print(Locale.new('root').Parent) --> nil

table Locale:GetChildren()
Gets all the children of the locale, also accpets Locale.GetChildren() (only for version ≤1.0.0a3)

table Locale:GetDescendants()
Gets all the children and its children of the locale, also accepts Locale.GetDescendants() (only for version ≤1.0.0a3)

Regional

These properties will return nil if no region is provided in the locale.

string Locale.MeasurementSystem
The measurement system used in the region of the locale, can either return metric, UK or US.

string Locale.TemperatureSystem
The temperature system used in the region of the locale, can either return metric, UK or US

string Locale.PaperSize
The paper size used in the region of the locale, can either return A4 or US-Letter

number Locale.WeekdayStart
The weekday the locale starts, 1 is Monday and 7 is Sunday.

number Locale.WeekendStart
The weekend of the locale starts, 1 is Monday and 7 is Sunday.

number Locale.WeekendEnd
The weekend of the locale ends, 1 is Monday and 7 is Sunday.

Territory

Terrtiory intl.Territory.new(string territory_code)
Creates a new territory

Territory intl.Territory.World
Territory intl.Territory.Africa
Territory intl.Territory.NorthAmerica
Territory intl.Territory.SouthAmerica
Territory intl.Territory.Oceania
Territory intl.Territory.WesternAfrica
Territory intl.Territory.CentralAmerica
Territory intl.Territory.EasternAfrica
Territory intl.Territory.NorthernAfrica
Territory intl.Territory.MiddleAfrica
Territory intl.Territory.SouthernAfrica
Territory intl.Territory.Americas
Territory intl.Territory.NorthernAmerica
Territory intl.Territory.Caribbean
Territory intl.Territory.EasternAsia
Territory intl.Territory.SouthernAsia
Territory intl.Territory.SoutheastAsia
Territory intl.Territory.SouthernEurope
Territory intl.Territory.Australasia
Territory intl.Territory.Melanesia
Territory intl.Territory.MicronesianRegion
Territory intl.Territory.Polynesia
Territory intl.Territory.Asia
Territory intl.Territory.CentralAsia
Territory intl.Territory.WesternAsia
Territory intl.Territory.Europe
Territory intl.Territory.EasternEurope
Territory intl.Territory.NorthernEurope
Territory intl.Territory.WesternEurope
Territory intl.Territory.SubSaharanAfrica
Territory intl.Territory.LatinAmerica
These are self-explanitory

Properties and class methods

string Territory.MeasurementSystem
The measurement system used in the territory

string Territory.TemperatureSystem
The temperature used in the territory

string Territory.PaperSize
The paper size used in the territory

The following below source are recieved through Unicode CLDR, If you want an accurate source, don’t use these

number Territory.GDP
The GDP of the territory

number Territory.Literacy
The literacy rate of the territory. (1 = 100%, 0.5 = 50%)

number Territory.Population
The population of the territory

table Territory:GetLanguages
Get all languages spoken in that territory

table Territory:GetOfficialLanguages
Gets all official langauges spoken in that territory

NumberFormat

NumberFormat intl.NumberFormat.new(Locale locale, table options)
Creates a NumberFormat userdata

Properties and class methods

NumberFormatParts NumberFormat:FormatToParts(number/BigInteger/BigNum value)
Formats the numbers in parts, to iter NumberFormatParts use NumberFormatParts:iter() instead of pairs(NumberFormatParts)

string NumberFormat:Format(number/BigInteger/BigNum value)
Formats the number based on the NumberFormat

DisplayNames

DisplayNames DisplayNames.new(Locale locale, table options)
Create a new locale display names

Properties and class methods

string DisplayNames:NameOf(Locale locale) (:of for versions ≤1.0.0a3)
Gets the display name of the locale paramter in the locale of the DisplayNames userdata.

DateTimeFormat

DateTimeFormat intl.DateTimeFormat.new(Locale locale, table options)
Create a DateTime format userdata

Properties and class methods

DateTimeFormatParts DateTimeFormat:FormatToParts(DateTime/os.date table date)
Formats the date in parts, to iter DateTimeFormatParts use DateTimeFormatParts:iter() instead of pairs(DateTimeFormatParts)

string DateTimeFormat:Format(DateTime/os.date table date)
Formats the dates based on the DateTimeFormat.

PluralRule

PluralRule intl.PluralRule.new(Locale locale, table options)
Creates a new pluralrule userdata.

Properties and class methods

string PluralRule:Select(number/BigInteger/BigNum value) (:select for versions ≤1.0.0a3)
Gets the plural of the locale, can either return zero, one, two, few, many or other

More parts will be documented later

Version 2.1 update

  • Formatting large numbers is now much faster.
  • useGrouping option is now enumerated, with 4 options (always, auto, min2 and never), true will map to always and false will map to never.
local formatter = Intl.NumberFormat.new('en', { useGrouping = "min2" });
print(formatter:Format(1234)) --> 1234
print(formatter:Format(12345)) --> 12,345
local formatter = Intl.NumberFormat.new('en');
print(formatter:Format(1234)) --> 1,234
print(formatter:Format(12345)) --> 12,345
  • -per- unit support.
  • Currency code is handled differently.

You can get it from here.

Version 2.2 update

  • FormatRange and FormatRangeToParts
  • Intl.toLocaleDateString and Intl.toLocaleTimeString added
  • Many bugs fixed and formatting numbers is faster.

You can get it from here

Note: I’m considering moving from JSON to XML version of Unicode CLDR, under three reasons:

  • The XML version seems to save up more space as oppoed to JSON (because the JSON version I’m currently using repeats information that can be inherited from its parent locale apparently)
  • It simply have more options, collators, plural ranges, these cannot be found on the JSON version I’m currently using. (I got the XML version for plural ranges)
  • It’s the official format CLDR uses.

Planned features:

  • Collator
  • Duration Format
  • Segementer (Added in 2.3)
  • Smart Unit preference
  • Repeating currency option for FormatRange
  • Rounding increments
  • supportedLocaleOf function on NumberFormat, DateTimeFormat, PluralRules, DisplayNames, RelativeTimeFormat, ListFormat, etc. (Added in 2.3)
  • More numbering system
  • ListFormat respecting items containg numbers, dates, etc.
  • Chinese, Ethopic, Persian calendars, etc.
  • Supported unit, numbering system, etc function.

and forgot one changelog for update 2.2:

  • Locale now resolves aliases for languages so Intl.Locale.new('eng') will now return en

2.0.0 changelog (as I’ll be updating the page):

  • Many features removed: Rule basde number formatting, locale text and line direction and regional stuff (but I’m working on another module that supports these)
  • ToLocaleString no longer hogs up memory as (for before 2.2) it creates a Intl.NumberFormat, as on version 2.0 all intldata will be garbage collected when no longer used. In version 2.2 this behaviour changed as it directly formats the number.
  • Locales are based on IETF BCP 47 tag (but -t- extension isn’t supported) and underscores in locales (en_US) are no longer valid.

Version 2.3 Update

  • Added Segmenter for text segmentation (not the best implementation)
  • SupportedLocaleOf
  • Takes much less space as I moved from JSON to XML version of Unicode CLDR.

You can get it from GitHub or from here:
All locale data version (9.3 MiB)
Sufficient locale data version (7.7 MiB)

Version 2.4 update

  • Added toLocaleLower and toLocaleUpper for language-senstiive case conversion.
  • Fixed compact rounding bug.
  • Chinese calendar added for date formatting (only from 1900 to 2100)

You can get it from GitHub or from here:
File (7.9 MiB) (All locale data version.rbxm (9.5 MiB))

Version 2.5 update

  • Fixed rounding bug for "floor" and "ceiling" for negative values, and added "up" and "down" rounding.
  • toLocaleTitle
  • :SelectRange for plural rules
  • Fixed signficaint digits when rounding values below zero.
  • Fixed ListFormat:FormatToParts() error.

GitHub or from here:
International.rbxm (9.5 MiB)

There’s only full version, apologies for any inconvinence.

Sorry, I know this are already 3 months ago, but I have a question: Could you explain how exactly this works? I find the idea really nice and was interested to see more. And how long did this toke you to do this? I am a little bit sad because it isn‘t really popular, but for me it should! Keep this up!

1 Like

Does this support number suffixes/is it possible/easier to make number suffixes like this?
e.g
function(100000) → 100k
function(10^5) → 100k
function(100,000) → 100k

What do you mean by number suffixes? (short/long) compact notation? (narrow/short/long) unit style? something else?
“Inherited” from EMCA 402 (which the API is based on) and Unicode ICU (which this library is influenced by), the library clearly supports compact notation (both short and long) and unit style (all narrow, short and long) if that’s what you mean.


Can you be more specific? As it’s a large library, it’d be here forever explaining all of them.
As this library is strongly influenced by Unicode ICU and ECMA 402 (this library actually has slightly more features than EMCA-402). Have you checked out Unicode ICU and Unicode CLDR? The specification for CLDR (do note this library uses CLDR 37, not CLDR 38 at the time of writing): UTS #35: Unicode Locale Data Markup Language

  • Extracting CLDR (to be specific CLDR 37) data took me roughly about a day
  • Implementing number format and date format initially (no bug fixes) took me roughly 8 hours.
  • Implementing locale system and locale datas initially took me roughly 4 hours.
  • Implementing display name and list format took me quite a short time.
  • Implementing plural rule took my roughly 4 hours.
1 Like

What I mean is like shortening numbers to “1m” or “100k” as example, is there functions for that that are easier than making your own function

What do you mean by number suffixes? (short/long) compact notation? (narrow/short/long) unit style? something else?
“Inherited” from EMCA 402 (which the API is based on) and Unicode ICU (which this library is influenced by), the library clearly supports compact notation (both short and long) and unit style (all narrow, short and long) if that’s what you mean.

1 Like

1m meaning 1 metre, 1 million or something else? 100k meaning 100 kilo, 100 thousand or something else?
shortening number meaning short compact notation?
If you meant compact notation (localised abbreviations corrosponding to the power of tens), you had to set the notation option to "compact"

local nf = NumberFormat.new('ja', { notation = "compact" })
print(nf:Format(10000)) --> 1万
local nf = NumberFormat.new('es', { notation = "compact" })
print(nf:Format(10000)) --> 10 mil

There isn’t a specific function just for a specific notation with a specific width; why do you need a specific function for this?
What do you mean by “easer than making your own function”?

1 Like

What I mean is, considering this library has number formatting, I was asking if there was a way to format numbers, like 100000 → 100k, and if its easier then making your own function for it like in
https://devforum.roblox.com/t/most-efficient-way-of-converting-a-number-in-scientific-notation-into-a-suffix-and-back/178431/3?u=complexpriorities

1 Like

This is pretty much the same as the JavaScript API:

local Intl = require(path.to.International)

-- I specified "en" as this will give you "K," "M," "B," "T," etc. but you could
-- specify nil here to automatically fetch the Player's locale
local i18n = Intl.NumberFormat.new("en", {
    minimumFractionDigits = 1, -- How many decimal places (minimum) to display; e.g. 12.0K
    maximumFractionDigits = 1, -- How many decimal places (maximum) to display; e.g. 12.5K
    notation = "compact",
    compactDisplay = "short",
})

print(i18n:Format(18472))
--> 18.5K

@Blockzez is there any way to configure the rounding? For example, if I wanted the above to always round down (to 18.4K until it actually reaches 18,500)? It would seem wrong to tell players they have 18.5K in cash when they actually only have 18,472. Also, awesome job! I can’t wait to use this in my own game. This has to be one of my favourite JavaScript APIs, and probably now a favourite in Roblox too.

2 Likes

There’s a rounding option that’s available for the NumberFormat API for this module I forget to mention, there are 6 options available:

  • "floor" - round down for positive, round up for negative
  • "ceiling" - round up for positive, round down for negative
  • "up" - roundup the value
  • "down" - truncate the value (round down the value)
  • "halfEven" - if it’s a midpoint, rounding it to the nearest even, otherwise round up if higher than midpoint or lower if it’s lower than midpoint (default)
  • "halfUp" - (probably the most familiar roudning mode), round down the value for values below the midpoint (.5), otherwise round it up.
1 Like

Very useful module, should get more appreciation :+1: