@@ -24,8 +24,8 @@ import (
2424 "github.com/gohugoio/hugo/common/herrors"
2525 "golang.org/x/text/language"
2626
27- "github.com/gohugoio/go-i18n/v2/i18n"
2827 "github.com/gohugoio/hugo/helpers"
28+ "github.com/nicksnyder/go-i18n/v2/i18n"
2929 toml "github.com/pelletier/go-toml/v2"
3030
3131 "github.com/gohugoio/hugo/deps"
@@ -50,12 +50,7 @@ func (tp *TranslationProvider) NewResource(dst *deps.Deps) error {
5050 if err != nil {
5151 defaultLangTag = language .English
5252 }
53- bundle := i18n .NewBundle (defaultLangTag )
54-
55- bundle .RegisterUnmarshalFunc ("toml" , toml .Unmarshal )
56- bundle .RegisterUnmarshalFunc ("yaml" , metadecoders .UnmarshalYaml )
57- bundle .RegisterUnmarshalFunc ("yml" , metadecoders .UnmarshalYaml )
58- bundle .RegisterUnmarshalFunc ("json" , json .Unmarshal )
53+ builder := newBundleBuilder (defaultLangTag )
5954
6055 w := hugofs .NewWalkway (
6156 hugofs.WalkwayConfig {
@@ -66,53 +61,140 @@ func (tp *TranslationProvider) NewResource(dst *deps.Deps) error {
6661 if info .IsDir () {
6762 return nil
6863 }
69- return addTranslationFile (bundle , source .NewFileInfo (info ))
64+ return builder . addTranslationFile (source .NewFileInfo (info ))
7065 },
7166 })
7267
7368 if err := w .Walk (); err != nil {
7469 return err
7570 }
7671
77- tp .t = NewTranslator (bundle , dst .Conf , dst .Log )
72+ bundle , err := builder .Build ()
73+ if err != nil {
74+ return err
75+ }
76+
77+ tp .t = newTranslator (bundle , dst .Conf , dst .Log )
7878
7979 dst .Translate = tp .t .Func (dst .Conf .Language ().Lang )
8080
8181 return nil
8282}
8383
84- const artificialLangTagPrefix = "art-x-"
84+ func newBundleBuilder (defaultLangTag language.Tag ) * bundleBuilder {
85+ b := i18n .NewBundle (defaultLangTag )
8586
86- func addTranslationFile (bundle * i18n.Bundle , r * source.File ) error {
87- f , err := r .FileInfo ().Meta ().Open ()
88- if err != nil {
89- return fmt .Errorf ("failed to open translations file %q:: %w" , r .LogicalName (), err )
87+ b .RegisterUnmarshalFunc ("toml" , toml .Unmarshal )
88+ b .RegisterUnmarshalFunc ("yaml" , metadecoders .UnmarshalYaml )
89+ b .RegisterUnmarshalFunc ("yml" , metadecoders .UnmarshalYaml )
90+ b .RegisterUnmarshalFunc ("json" , json .Unmarshal )
91+
92+ bb := & bundle {
93+ b : b ,
94+ definedLangs : make (map [language.Tag ]bool ),
95+ undefinedLangs : make (map [language.Tag ]string ),
9096 }
9197
92- b := helpers .ReaderToBytes (f )
93- f .Close ()
98+ return & bundleBuilder {b : bb }
99+ }
100+
101+ type bundleBuilder struct {
102+ b * bundle
103+
104+ // The Go i18n library we use does not support artificial language tags.
105+ // Store them away and add them later using available real language tags.
106+ undefinedLangs []* source.File
107+ }
108+
109+ type bundle struct {
110+ b * i18n.Bundle
111+
112+ // Bundled languages.
113+ definedLangs map [language.Tag ]bool
114+
115+ // Maps asn arbitrary but real language tag to a Hugo artificial language key.
116+ undefinedLangs map [language.Tag ]string
117+ }
118+
119+ var undefinedLangErr = fmt .Errorf ("undefined language" )
120+
121+ func (b * bundleBuilder ) Build () (* bundle , error ) {
122+ const retries = 10
123+ for range retries {
124+ if len (b .undefinedLangs ) == 0 {
125+ break
126+ }
127+ var undefinedLangs []* source.File
128+ for _ , r := range b .undefinedLangs {
129+ name := r .LogicalName ()
130+ lang := paths .Filename (name )
131+ var tag language.Tag
132+ // Find an unused language tag.
133+ for _ , t := range languageTags {
134+ if ! b .b .definedLangs [t ] {
135+ tag = t
136+ break
137+ }
138+ }
139+ if tag == language .Und {
140+ return nil , fmt .Errorf ("failed to resolve language for file %q" , r .LogicalName ())
141+ }
142+ b .b .undefinedLangs [tag ] = lang
143+ ext := paths .Ext (name )
144+ name = tag .String () + ext
145+ if err := b .doAddTranslationFile (r , tag , name ); err != nil {
146+ if err == undefinedLangErr {
147+ undefinedLangs = append (undefinedLangs , r )
148+ continue
149+ }
150+ return nil , err
151+ }
152+ }
153+ b .undefinedLangs = undefinedLangs
154+ }
94155
156+ if len (b .undefinedLangs ) != 0 {
157+ return nil , fmt .Errorf ("failed to resolve languages for some translation files" )
158+ }
159+
160+ return b .b , nil
161+ }
162+
163+ func (bb * bundleBuilder ) addTranslationFile (r * source.File ) error {
95164 name := r .LogicalName ()
96165 lang := paths .Filename (name )
97166 tag := language .Make (lang )
98167 if tag == language .Und {
99- try := artificialLangTagPrefix + lang
100- _ , err = language .Parse (try )
101- if err != nil {
102- return fmt .Errorf ("%q: %s" , try , err )
103- }
104- name = artificialLangTagPrefix + name
168+ bb .undefinedLangs = append (bb .undefinedLangs , r )
169+ return nil
170+ }
171+ err := bb .doAddTranslationFile (r , tag , name )
172+
173+ if err == undefinedLangErr {
174+ bb .undefinedLangs = append (bb .undefinedLangs , r )
175+ return nil
105176 }
106177
107- _ , err = bundle .ParseMessageFileBytes (b , name )
178+ return err
179+ }
180+
181+ // Note that name must include the file extension.
182+ func (bb * bundleBuilder ) doAddTranslationFile (r * source.File , tag language.Tag , name string ) error {
183+ bb .b .definedLangs [tag ] = true
184+
185+ f , err := r .FileInfo ().Meta ().Open ()
186+ if err != nil {
187+ return fmt .Errorf ("failed to open translations file %q:: %w" , r .LogicalName (), err )
188+ }
189+
190+ b := helpers .ReaderToBytes (f )
191+ f .Close ()
192+
193+ _ , err = bb .b .b .ParseMessageFileBytes (b , name )
108194 if err != nil {
109195 if strings .Contains (err .Error (), "no plural rule" ) {
110196 // https://github.com/gohugoio/hugo/issues/7798
111- name = artificialLangTagPrefix + name
112- _ , err = bundle .ParseMessageFileBytes (b , name )
113- if err == nil {
114- return nil
115- }
197+ return undefinedLangErr
116198 }
117199 return errWithFileContext (fmt .Errorf ("failed to load translations: %w" , err ), r )
118200 }
@@ -137,3 +219,85 @@ func errWithFileContext(inerr error, r *source.File) error {
137219
138220 return herrors .NewFileErrorFromName (inerr , realFilename ).UpdateContent (f , nil )
139221}
222+
223+ var languageTags = []language.Tag {
224+ language .Afrikaans ,
225+ language .Amharic ,
226+ language .Arabic ,
227+ language .ModernStandardArabic ,
228+ language .Azerbaijani ,
229+ language .Bulgarian ,
230+ language .Bengali ,
231+ language .Catalan ,
232+ language .Czech ,
233+ language .Danish ,
234+ language .German ,
235+ language .Greek ,
236+ language .English ,
237+ language .AmericanEnglish ,
238+ language .BritishEnglish ,
239+ language .Spanish ,
240+ language .EuropeanSpanish ,
241+ language .LatinAmericanSpanish ,
242+ language .Estonian ,
243+ language .Persian ,
244+ language .Finnish ,
245+ language .Filipino ,
246+ language .French ,
247+ language .CanadianFrench ,
248+ language .Gujarati ,
249+ language .Hebrew ,
250+ language .Hindi ,
251+ language .Croatian ,
252+ language .Hungarian ,
253+ language .Armenian ,
254+ language .Indonesian ,
255+ language .Icelandic ,
256+ language .Italian ,
257+ language .Japanese ,
258+ language .Georgian ,
259+ language .Kazakh ,
260+ language .Khmer ,
261+ language .Kannada ,
262+ language .Korean ,
263+ language .Kirghiz ,
264+ language .Lao ,
265+ language .Lithuanian ,
266+ language .Latvian ,
267+ language .Macedonian ,
268+ language .Malayalam ,
269+ language .Mongolian ,
270+ language .Marathi ,
271+ language .Malay ,
272+ language .Burmese ,
273+ language .Nepali ,
274+ language .Dutch ,
275+ language .Norwegian ,
276+ language .Punjabi ,
277+ language .Polish ,
278+ language .Portuguese ,
279+ language .BrazilianPortuguese ,
280+ language .EuropeanPortuguese ,
281+ language .Romanian ,
282+ language .Russian ,
283+ language .Sinhala ,
284+ language .Slovak ,
285+ language .Slovenian ,
286+ language .Albanian ,
287+ language .Serbian ,
288+ language .SerbianLatin ,
289+ language .Swedish ,
290+ language .Swahili ,
291+ language .Tamil ,
292+ language .Telugu ,
293+ language .Thai ,
294+ language .Turkish ,
295+ language .Ukrainian ,
296+ language .Urdu ,
297+ language .Uzbek ,
298+ language .Vietnamese ,
299+ language .Chinese ,
300+ language .SimplifiedChinese ,
301+ language .TraditionalChinese ,
302+ language .Zulu ,
303+ }
0 commit comments