diff --git a/docs/content/content/multilingual.md b/docs/content/content/multilingual.md index 86da75409..d3f69f2e6 100644 --- a/docs/content/content/multilingual.md +++ b/docs/content/content/multilingual.md @@ -86,7 +86,7 @@ slowly start to translate your current content without having to rename everythi If left unspecified, the value for `DefaultContentLanguage` defaults to `en`. -By having the same _base file name_, the content pieces are linked together as translated pieces. +By having the same _base file name_, the content pieces are linked together as translated pieces. ### Link to translated content @@ -190,17 +190,16 @@ The rendering of the main navigation works as usual. `.Site.Menus` will just con ``` -An empty string will be shown if the translation for the current language is missing and no default value is set. +### Missing translations -While translating a Hugo website it can be handy to have a visual indicator as well. The `EnableMissingTranslationPlaceholders` config option allows you to replace the empty string with a placeholder like `[i18n] identifier`, where `identifier` is the id of the missing translation. +If a string does not have a translation for the current language, Hugo will use the value from the default language. If no default value is set, an empty string will be shown. + +While translating a Hugo site, it can be handy to have a visual indicator of missing translations. The `EnableMissingTranslationPlaceholders` config option will flag all untranslated strings with the placeholder `[i18n] identifier`, where `identifier` is the id of the missing translation. **Remember: Hugo will generate your website with these placeholders. It might not be suited for production environments.** - ### Multilingual Themes support To support Multilingual mode in your themes, some considerations must be taken for the URLs in the templates. If there are more than one language, URLs must either come from the built-in `.Permalink` or `.URL`, be constructed with `relLangURL` or `absLangURL` template funcs -- or prefixed with `{{.LanguagePrefix }}`. If there are more than one language defined, the`LanguagePrefix` variable will equal `"/en"` (or whatever your `CurrentLanguage` is). If not enabled, it will be an empty string, so it is harmless for single-language sites. - - diff --git a/docs/content/overview/configuration.md b/docs/content/overview/configuration.md index 259d9acfb..4ba00664d 100644 --- a/docs/content/overview/configuration.md +++ b/docs/content/overview/configuration.md @@ -135,7 +135,7 @@ along with their current, default values: # Enable Emoji emoticons support for page content. # See www.emoji-cheat-sheet.com enableEmoji: false - # Show a placeholder like "[i18n] foo" instead of an empty string if a translation is missing + # Show a placeholder instead of the default value or an empty string if a translation is missing enableMissingTranslationPlaceholders: false footnoteAnchorPrefix: "" footnoteReturnLinkContents: "" diff --git a/tpl/template_i18n_test.go b/tpl/template_i18n_test.go index 4578005af..d6b8d967a 100644 --- a/tpl/template_i18n_test.go +++ b/tpl/template_i18n_test.go @@ -22,15 +22,94 @@ import ( ) type test struct { - file string - content []byte + data map[string][]byte + args interface{} + lang, id, expected, expected_flag string } -func doTestI18nTranslate(t *testing.T, data []test, lang, id string) string { +var tests []test = []test{ + // All translations present + { + data: map[string][]byte{ + "en.yaml": []byte("- id: \"hello\"\n translation: \"Hello, World!\""), + "es.yaml": []byte("- id: \"hello\"\n translation: \"¡Hola, Mundo!\""), + }, + args: nil, + lang: "es", + id: "hello", + expected: "¡Hola, Mundo!", + expected_flag: "¡Hola, Mundo!", + }, + // Translation missing in current language but present in default + { + data: map[string][]byte{ + "en.yaml": []byte("- id: \"hello\"\n translation: \"Hello, World!\""), + "es.yaml": []byte("- id: \"goodbye\"\n translation: \"¡Adiós, Mundo!\""), + }, + args: nil, + lang: "es", + id: "hello", + expected: "Hello, World!", + expected_flag: "[i18n] hello", + }, + // Translation missing in default language but present in current + { + data: map[string][]byte{ + "en.yaml": []byte("- id: \"goodybe\"\n translation: \"Goodbye, World!\""), + "es.yaml": []byte("- id: \"hello\"\n translation: \"¡Hola, Mundo!\""), + }, + args: nil, + lang: "es", + id: "hello", + expected: "¡Hola, Mundo!", + expected_flag: "¡Hola, Mundo!", + }, + // Translation missing in both default and current language + { + data: map[string][]byte{ + "en.yaml": []byte("- id: \"goodbye\"\n translation: \"Goodbye, World!\""), + "es.yaml": []byte("- id: \"goodbye\"\n translation: \"¡Adiós, Mundo!\""), + }, + args: nil, + lang: "es", + id: "hello", + expected: "", + expected_flag: "[i18n] hello", + }, + // Default translation file missing or empty + { + data: map[string][]byte{ + "en.yaml": []byte(""), + }, + args: nil, + lang: "es", + id: "hello", + expected: "", + expected_flag: "[i18n] hello", + }, + // Context provided + { + data: map[string][]byte{ + "en.yaml": []byte("- id: \"wordCount\"\n translation: \"Hello, {{.WordCount}} people!\""), + "es.yaml": []byte("- id: \"wordCount\"\n translation: \"¡Hola, {{.WordCount}} gente!\""), + }, + args: struct { + WordCount int + }{ + 50, + }, + lang: "es", + id: "wordCount", + expected: "¡Hola, 50 gente!", + expected_flag: "¡Hola, 50 gente!", + }, +} + +func doTestI18nTranslate(t *testing.T, data map[string][]byte, lang, id string, args interface{}) string { i18nBundle := bundle.New() - for _, r := range data { - err := i18nBundle.ParseTranslationFileBytes(r.file, r.content) + for file, content := range data { + err := i18nBundle.ParseTranslationFileBytes(file, content) if err != nil { t.Errorf("Error parsing translation file: %s", err) } @@ -39,7 +118,7 @@ func doTestI18nTranslate(t *testing.T, data []test, lang, id string) string { SetI18nTfuncs(i18nBundle) SetTranslateLang(lang) - translated, err := I18nTranslate(id, nil) + translated, err := I18nTranslate(id, args) if err != nil { t.Errorf("Error translating '%s': %s", id, err) } @@ -47,7 +126,6 @@ func doTestI18nTranslate(t *testing.T, data []test, lang, id string) string { } func TestI18nTranslate(t *testing.T) { - var data []test var actual, expected string viper.SetDefault("DefaultContentLanguage", "en") @@ -56,60 +134,14 @@ func TestI18nTranslate(t *testing.T) { for _, enablePlaceholders := range []bool{false, true} { viper.Set("EnableMissingTranslationPlaceholders", enablePlaceholders) - // All translations present - data = []test{ - {"en.yaml", []byte("- id: \"hello\"\n translation: \"Hello, World!\"")}, - {"es.yaml", []byte("- id: \"hello\"\n translation: \"¡Hola, Mundo!\"")}, + for _, test := range tests { + if enablePlaceholders { + expected = test.expected_flag + } else { + expected = test.expected + } + actual = doTestI18nTranslate(t, test.data, test.lang, test.id, test.args) + assert.Equal(t, expected, actual) } - expected = "¡Hola, Mundo!" - actual = doTestI18nTranslate(t, data, "es", "hello") - assert.Equal(t, expected, actual) - - // Translation missing in current language but present in default - data = []test{ - {"en.yaml", []byte("- id: \"hello\"\n translation: \"Hello, World!\"")}, - {"es.yaml", []byte("- id: \"goodbye\"\n translation: \"¡Adiós, Mundo!\"")}, - } - if enablePlaceholders { - expected = "[i18n] hello" - } else { - expected = "Hello, World!" - } - actual = doTestI18nTranslate(t, data, "es", "hello") - assert.Equal(t, expected, actual) - - // Translation missing in default language but present in current - data = []test{ - {"en.yaml", []byte("- id: \"goodbye\"\n translation: \"Goodbye, World!\"")}, - {"es.yaml", []byte("- id: \"hello\"\n translation: \"¡Hola, Mundo!\"")}, - } - expected = "¡Hola, Mundo!" - actual = doTestI18nTranslate(t, data, "es", "hello") - assert.Equal(t, expected, actual) - - // Translation missing in both default and current language - data = []test{ - {"en.yaml", []byte("- id: \"goodbye\"\n translation: \"Goodbye, World!\"")}, - {"es.yaml", []byte("- id: \"goodbye\"\n translation: \"¡Adiós, Mundo!\"")}, - } - if enablePlaceholders { - expected = "[i18n] hello" - } else { - expected = "" - } - actual = doTestI18nTranslate(t, data, "es", "hello") - assert.Equal(t, expected, actual) - - // Default translation file missing or empty - data = []test{ - {"en.yaml", []byte("")}, - } - actual = doTestI18nTranslate(t, data, "es", "hello") - if enablePlaceholders { - expected = "[i18n] hello" - } else { - expected = "" - } - assert.Equal(t, expected, actual) } }