Skip to content

Commit 057b443

Browse files
committed
deps: Move from github.com/gohugoio/go-i18n/v2 => github.com/nicksnyder/go-i18n/v2
Closes #14064
1 parent 5bad0d5 commit 057b443

File tree

5 files changed

+218
-41
lines changed

5 files changed

+218
-41
lines changed

‎go.mod‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ require (
156156
github.com/mattn/go-runewidth v0.0.16 // indirect
157157
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
158158
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
159+
github.com/nicksnyder/go-i18n/v2 v2.6.0 // indirect
159160
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect
160161
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect
161162
github.com/olekukonko/errors v1.1.0 // indirect

‎go.sum‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,8 @@ github.com/muesli/smartcrop v0.3.0/go.mod h1:i2fCI/UorTfgEpPPLWiFBv4pye+YAG78Rwc
432432
github.com/neurosnap/sentences v1.0.6/go.mod h1:pg1IapvYpWCJJm/Etxeh0+gtMf1rI1STY9S7eUCPbDc=
433433
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
434434
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
435+
github.com/nicksnyder/go-i18n/v2 v2.6.0 h1:C/m2NNWNiTB6SK4Ao8df5EWm3JETSTIGNXBpMJTxzxQ=
436+
github.com/nicksnyder/go-i18n/v2 v2.6.0/go.mod h1:88sRqr0C6OPyJn0/KRNaEz1uWorjxIKP7rUUcvycecE=
435437
github.com/niklasfasching/go-org v1.9.1 h1:/3s4uTPOF06pImGa2Yvlp24yKXZoTYM+nsIlMzfpg/0=
436438
github.com/niklasfasching/go-org v1.9.1/go.mod h1:ZAGFFkWvUQcpazmi/8nHqwvARpr1xpb+Es67oUGX/48=
437439
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY=

‎langs/i18n/i18n.go‎

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import (
2626
"github.com/gohugoio/hugo/config"
2727
"github.com/gohugoio/hugo/resources/page"
2828

29-
"github.com/gohugoio/go-i18n/v2/i18n"
29+
"github.com/nicksnyder/go-i18n/v2/i18n"
3030
)
3131

3232
type translateFunc func(ctx context.Context, translationID string, templateData any) string
@@ -38,8 +38,8 @@ type Translator struct {
3838
logger loggers.Logger
3939
}
4040

41-
// NewTranslator creates a new Translator for the given language bundle and configuration.
42-
func NewTranslator(b *i18n.Bundle, cfg config.AllProvider, logger loggers.Logger) Translator {
41+
// newTranslator creates a new Translator for the given language bundle and configuration.
42+
func newTranslator(b *bundle, cfg config.AllProvider, logger loggers.Logger) Translator {
4343
t := Translator{cfg: cfg, logger: logger, translateFuncs: make(map[string]translateFunc)}
4444
t.initFuncs(b)
4545
return t
@@ -62,14 +62,21 @@ func (t Translator) Func(lang string) translateFunc {
6262
}
6363
}
6464

65-
func (t Translator) initFuncs(bndl *i18n.Bundle) {
65+
func (t Translator) initFuncs(bndl *bundle) {
6666
enableMissingTranslationPlaceholders := t.cfg.EnableMissingTranslationPlaceholders()
67-
for _, lang := range bndl.LanguageTags() {
68-
currentLang := lang
69-
currentLangStr := currentLang.String()
70-
// This may be pt-BR; make it case insensitive.
71-
currentLangKey := strings.ToLower(strings.TrimPrefix(currentLangStr, artificialLangTagPrefix))
72-
localizer := i18n.NewLocalizer(bndl, currentLangStr)
67+
for _, lang := range bndl.b.LanguageTags() {
68+
currentLangTag := lang
69+
currentLangTagStr := currentLangTag.String()
70+
71+
var currentLangKey string
72+
if undefinedLangKey, found := bndl.undefinedLangs[currentLangTag]; found {
73+
currentLangKey = undefinedLangKey
74+
} else {
75+
// This may be pt-BR; make it case insensitive.
76+
currentLangKey = strings.ToLower(currentLangTagStr)
77+
}
78+
79+
localizer := i18n.NewLocalizer(bndl.b, currentLangTagStr)
7380
t.translateFuncs[currentLangKey] = func(ctx context.Context, translationID string, templateData any) string {
7481
pluralCount := getPluralCount(templateData)
7582

@@ -99,7 +106,7 @@ func (t Translator) initFuncs(bndl *i18n.Bundle) {
99106
PluralCount: pluralCount,
100107
})
101108

102-
sameLang := currentLang == translatedLang
109+
sameLang := currentLangTag == translatedLang
103110

104111
if err == nil && sameLang {
105112
return translated
@@ -116,11 +123,11 @@ func (t Translator) initFuncs(bndl *i18n.Bundle) {
116123
}
117124

118125
if _, ok := err.(*i18n.MessageNotFoundErr); !ok {
119-
t.logger.Warnf("Failed to get translated string for language %q and ID %q: %s", currentLangStr, translationID, err)
126+
t.logger.Warnf("Failed to get translated string for language %q and ID %q: %s", currentLangTagStr, translationID, err)
120127
}
121128

122129
if t.cfg.PrintI18nWarnings() {
123-
t.logger.Warnf("i18n|MISSING_TRANSLATION|%s|%s", currentLangStr, translationID)
130+
t.logger.Warnf("i18n|MISSING_TRANSLATION|%s|%s", currentLangTagStr, translationID)
124131
}
125132

126133
if enableMissingTranslationPlaceholders {

‎langs/i18n/i18n_test.go‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,9 @@ func TestI18nTranslate(t *testing.T) {
488488
v.Set("enableMissingTranslationPlaceholders", enablePlaceholders)
489489

490490
for _, test := range i18nTests {
491+
if enablePlaceholders || test.name != "unknown-language-codes" {
492+
// continue
493+
}
491494
c.Run(fmt.Sprintf("%s-%t", test.name, enablePlaceholders), func(c *qt.C) {
492495
if enablePlaceholders {
493496
expected = test.expectedFlag

‎langs/i18n/translationProvider.go‎

Lines changed: 192 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)