Shopify / Shopify/worldwide

Gregorian#month_names and #weekday_names return partial data for 104 locales (en-CA, en-GB, en-IN, ...)

Open
#581 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Ruby
Stars
97
Forks
17
Avg merge
13h 33m
Merged PRs (30d)
11

Description

Summary

Worldwide::Calendar::Gregorian.month_names and .weekday_names return partial results for 104 locales, including en-CA, en-GB, en-IN, en-AU, en-NZ, en-ZA and every other en-001 descendant.

Worldwide::Calendar::Gregorian.month_names(locale: "en-CA", width: :abbreviated)
#=> ["Sept"]
# expected: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"]

Worldwide::Calendar::Gregorian.weekday_names(locale: "se-FI")
#=> {mon: "mánnodat", tue: "disdat", thu: "duorastat", sat: "lávvordat"}
# expected: all seven, with sun/wed/fri inherited from `se`

Reproduced on worldwide 1.26.2 (current main, ad6eeefb), i18n 1.15.2, Ruby 3.4.8.

Cause

CLDR resolves locale data item by item: the effective data for a locale is the union of its own data with each of its ancestors', with the more specific locale winning per item (UTS #35 §4.1). Locale files in data/cldr/locales/ are stored as deltas accordingly — data/cldr/locales/en-CA/calendars.yml contains exactly:

months:
  stand_alone:
    abbreviated:
      9: Sept

I18n::Backend::Fallbacks#translate returns the first non-nil node it finds in the fallback chain. That is correct for leaves (months.stand_alone.abbreviated.1 still resolves to "Jan" from en) but wrong for a whole node: months.stand_alone.abbreviated matches en-CA's one-entry hash and the eleven inherited entries never get merged in.

month_names then calls .values on that hash, so the truncation is silent — lib/worldwide/calendar/gregorian.rb#L20.

Affected locales

805 known locales × 2 methods × 3 widths = 4830 combinations; 108 of them are wrong, spanning 104 distinct locales.

Method Width Count Locales
month_names :abbreviated 101 ar-DZ, ar-MA, ar-TN, en-001, en-150, en-AG, en-AI, en-AT, en-BB, en-BE, en-BM, en-BS, en-BW, en-BZ, en-CA, en-CC, en-CH, en-CK, en-CM, en-CX, en-CY, en-DE, en-DG, en-DK, en-DM, en-ER, en-FI, en-FJ, en-FK, en-FM, en-GD, en-GG, en-GH, en-GI, en-GM, en-GY, en-HK, en-IE, en-IL, en-IM, en-IN, en-IO, en-JE, en-JM, en-KE, en-KI, en-KN, en-KY, en-LC, en-LR, en-LS, en-MG, en-MO, en-MS, en-MT, en-MU, en-MV, en-MW, en-MY, en-NA, en-NF, en-NG, en-NL, en-NR, en-NU, en-NZ, en-PG, en-PK, en-PN, en-PW, en-RW, en-SB, en-SC, en-SD, en-SE, en-SG, en-SH, en-SI, en-SL, en-SS, en-SX, en-SZ, en-TC, en-TK, en-TO, en-TT, en-TV, en-TZ, en-UG, en-VC, en-VG, en-VU, en-WS, en-ZA, en-ZM, en-ZW, hi-Latn, hi-Latn-IN, se-FI, sr-Latn-ME, sr-Latn-XK
month_names :narrow 3 ar-DZ, ar-MA, ar-TN
month_names :wide 3 ar-DZ, ar-MA, ar-TN
weekday_names :wide 1 se-FI

Worst cases return a single entry: hi-Latn and se-FI return 1 of 12 abbreviated month names; sr-Latn-ME and sr-Latn-XK return 5 of 12. en-GB and friends return 1 of 12.

Secondary issue: missing structural keys degrade into a plausible string

Worldwide::I18nExceptionHandler#degraded_translation (lib/worldwide/i18n_exception_handler.rb#L51-L57) turns any unresolved key into a humanized copy of its last segment. For a structural lookup that means a missing weekday table comes back as the string "wide" rather than raising:

Worldwide::Cldr.t("calendars.gregorian.days.stand_alone.wide", locale: :some_locale_without_data)
#=> "wide"

That is a reasonable default for leaf strings in a UI, but for a hash-valued CLDR node it converts a data gap into a plausible-looking value, and month_names then fails with NoMethodError: undefined method 'values' for an instance of String some distance from the cause. (This is how the same call sites failed on worldwide < 1.25.6, before the fiber-storage fix in 76762d19 restored the CLDR fallback chain on Ruby 3.2+.)

Proposed fix

Resolve structural CLDR keys with CLDR's inheritance rules — walk the fallback chain from the least specific ancestor to the most specific, deep-merging each locale's own contribution, and resolve <alias> nodes against the requested locale rather than the ancestor they were found in. Then have weekday_names / month_names raise if an entry is still missing rather than returning degraded data.

PR: #NNN

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Read lib/worldwide/calendar/gregorian.rb and lib/worldwide/i18n_exception_handler.rb, then reproduce the en-CA and se-FI examples using the current CLDR fallback behavior. Trace I18n::Backend::Fallbacks#translate for structural keys and compare the affected locale data in data/cldr/locales/. Done means inherited entries are present for the listed methods and widths, while missing structural data raises instead of degrading to a string.

Written by the indexing model from the issue text.

Assessment

Tech stack
ruby
Domain
internationalization, localization
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.