@@ -171,6 +171,96 @@ defmodule Localize.Locale.Provider do
171171 Application . get_env ( :localize , :locale_provider , Localize.Locale.Provider.PersistentTerm )
172172 end
173173
174+ @ doc """
175+ Loads locale data with fallback through the CLDR parent chain.
176+
177+ Attempts to load the requested locale via `provider.load/1`. If the
178+ load fails, walks up the CLDR locale inheritance chain (e.g.
179+ `en-AU` → `en` → `und`) trying each parent in turn. If the entire
180+ chain is exhausted without success, falls back to `:en` which is
181+ guaranteed to be present.
182+
183+ Returns `{:ok, locale_data, resolved_locale_id}` so the caller
184+ knows which locale was actually loaded.
185+
186+ ### Arguments
187+
188+ * `provider` is the module implementing `Localize.Locale.Provider`.
189+
190+ * `locale` is a locale identifier atom or a `t:Localize.LanguageTag.t/0`.
191+
192+ ### Returns
193+
194+ * `{:ok, locale_data, resolved_locale_id}` on success.
195+
196+ * `{:error, exception}` if even the `:en` fallback fails (should not
197+ happen in normal operation).
198+
199+ """
200+ @ spec load_with_fallback ( module ( ) , locale ( ) ) ::
201+ { :ok , map ( ) , locale_id ( ) } | { :error , Exception . t ( ) }
202+ def load_with_fallback ( provider , locale ) do
203+ locale_id = Localize.Locale . to_locale_id ( locale )
204+
205+ case provider . load ( locale ) do
206+ { :ok , locale_data } ->
207+ { :ok , locale_data , locale_id }
208+
209+ { :error , _exception } ->
210+ require Logger
211+
212+ Logger . debug (
213+ "Locale #{ inspect ( locale_id ) } not available, walking parent chain" ,
214+ domain: [ :localize ]
215+ )
216+
217+ walk_parent_chain ( provider , locale_id )
218+ end
219+ end
220+
221+ defp walk_parent_chain ( provider , locale_id ) do
222+ require Logger
223+
224+ case Localize.Locale . parent ( to_string ( locale_id ) ) do
225+ { :ok , parent_tag } ->
226+ parent_id = Localize.Locale . to_locale_id ( parent_tag )
227+
228+ Logger . debug (
229+ "Trying parent locale #{ inspect ( parent_id ) } for #{ inspect ( locale_id ) } " ,
230+ domain: [ :localize ]
231+ )
232+
233+ case provider . load ( parent_id ) do
234+ { :ok , locale_data } ->
235+ { :ok , locale_data , parent_id }
236+
237+ { :error , _ } ->
238+ walk_parent_chain ( provider , parent_id )
239+ end
240+
241+ { :error , _ } ->
242+ # Reached root (und) with no success — fall back to :en
243+ fallback_to_en ( provider , locale_id )
244+ end
245+ end
246+
247+ defp fallback_to_en ( provider , original_locale_id ) do
248+ require Logger
249+
250+ Logger . debug (
251+ "Parent chain exhausted for #{ inspect ( original_locale_id ) } , falling back to :en" ,
252+ domain: [ :localize ]
253+ )
254+
255+ case provider . load ( :en ) do
256+ { :ok , locale_data } ->
257+ { :ok , locale_data , :en }
258+
259+ { :error , _ } = error ->
260+ error
261+ end
262+ end
263+
174264 @ doc """
175265 Returns the directory in which downloaded locale data is cached.
176266
0 commit comments