From 570591a8dc808be1763f778b38554c7db9fab99f Mon Sep 17 00:00:00 2001 From: CHIKAMATSU Naohiro Date: Mon, 2 Sep 2024 19:59:35 +0900 Subject: [PATCH] Introduce russian message file --- README.md | 11 ++++++++- csv.go | 25 ++++++++++++++------- example_test.go | 34 +++++++++++++++++++++++++++- i18n/ru.yaml | 59 +++++++++++++++++++++++++++++++++++++++++++++++++ option.go | 8 +++++++ 5 files changed, 127 insertions(+), 10 deletions(-) create mode 100644 i18n/ru.yaml diff --git a/README.md b/README.md index 14bc3a0..ff18b34 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,16 @@ The csv package is a library for performing validation when reading CSV or TSV files. Validation rules are specified using struct tags. The csv package read returns which columns of which rows do not adhere to the specified rules. -We are implementing internationalization (i18n) for error messages to make them easier for non-engineers to understand. +We are implementing internationalization (i18n) for error messages. + +### Supported languages + +- English +- Japanese +- Russian + +If you want to add a new language, please create a pull request. +Ref. https://github.com/nao1215/csv/pull/8 ## Why need csv package? diff --git a/csv.go b/csv.go index fd257d6..170c30e 100644 --- a/csv.go +++ b/csv.go @@ -52,15 +52,10 @@ func NewCSV(r io.Reader, opts ...Option) (*CSV, error) { csv := &CSV{ reader: csv.NewReader(r), } - csv.i18nBundle = i18n.NewBundle(language.English) - csv.i18nBundle.RegisterUnmarshalFunc("yaml", yaml.Unmarshal) - if _, err := csv.i18nBundle.LoadMessageFileFS(LocaleFS, "i18n/en.yaml"); err != nil { - return nil, NewError(csv.i18nLocalizer, "ErrLoadMessageFile", err.Error()) - } - if _, err := csv.i18nBundle.LoadMessageFileFS(LocaleFS, "i18n/ja.yaml"); err != nil { - return nil, NewError(csv.i18nLocalizer, "ErrLoadMessageFile", err.Error()) + + if err := csv.newI18n(); err != nil { + return nil, err } - csv.i18nLocalizer = i18n.NewLocalizer(csv.i18nBundle, "en") for _, opt := range opts { if err := opt(csv); err != nil { @@ -70,6 +65,20 @@ func NewCSV(r io.Reader, opts ...Option) (*CSV, error) { return csv, nil } +// newI18n initializes the i18n bundle and localizer. +func (c *CSV) newI18n() error { + c.i18nBundle = i18n.NewBundle(language.English) + c.i18nBundle.RegisterUnmarshalFunc("yaml", yaml.Unmarshal) + + for _, lang := range []string{"en", "ja", "ru"} { + if _, err := c.i18nBundle.LoadMessageFileFS(LocaleFS, fmt.Sprintf("i18n/%s.yaml", lang)); err != nil { + return NewError(c.i18nLocalizer, "ErrLoadMessageFile", err.Error()) + } + } + c.i18nLocalizer = i18n.NewLocalizer(c.i18nBundle, "en") + return nil +} + // Decode reads the CSV and returns the columns that have syntax errors on a per-line basis. // The strutSlicePointer is a pointer to structure slice where validation rules are set in struct tags. func (c *CSV) Decode(structSlicePointer any) []error { diff --git a/example_test.go b/example_test.go index 76671cd..30059fa 100644 --- a/example_test.go +++ b/example_test.go @@ -41,7 +41,7 @@ a,Yulia,25 // line:4 column name: target is not an alphabetic character: value=Den1s } -func ExampleCSV_InJapanese() { +func ExampleWithJapaneseLanguage() { input := `id,name,age 1,Gina,23 a,Yulia,25 @@ -72,3 +72,35 @@ a,Yulia,25 // line:3 column id: ターゲットが数字ではありません: value=a // line:4 column name: ターゲットがアルファベット文字ではありません: value=Den1s } + +func ExampleWithRussianLanguage() { + input := `id,name,age +1,Gina,23 +a,Yulia,25 +3,Den1s,30 +` + buf := bytes.NewBufferString(input) + c, err := csv.NewCSV(buf, csv.WithRussianLanguage()) + if err != nil { + panic(err) + } + + type person struct { + ID int `validate:"numeric"` + Name string `validate:"alpha"` + Age int `validate:"gt=24"` + } + people := make([]person, 0) + + errs := c.Decode(&people) + if len(errs) != 0 { + for _, err := range errs { + fmt.Println(err.Error()) + } + } + + // Output: + // line:2 column age: целевое значение не больше порогового значения: threshold=24, value=23 + // line:3 column id: целевое значение не является числовым символом: value=a + // line:4 column name: целевое значение не является алфавитным символом: value=Den1s +} diff --git a/i18n/ru.yaml b/i18n/ru.yaml new file mode 100644 index 0000000..c8af23e --- /dev/null +++ b/i18n/ru.yaml @@ -0,0 +1,59 @@ +- id: "ErrStructSlicePointer" + translation: "значение не является указателем на срез структур" + +- id: "ErrInvalidOneOfFormat" + translation: "целевое значение не является одним из допустимых значений" + +- id: "ErrInvalidThresholdFormat" + translation: "неверный формат порогового значения" + +- id: "ErrInvalidBoolean" + translation: "целевое значение не является булевым типом" + +- id: "ErrInvalidAlphabet" + translation: "целевое значение не является алфавитным символом" + +- id: "ErrInvalidNumeric" + translation: "целевое значение не является числовым символом" + +- id: "ErrInvalidAlphanumeric" + translation: "целевое значение не является буквенно-цифровым символом" + +- id: "ErrRequired" + translation: "целевое значение обязательно, но оно пустое" + +- id: "ErrEqual" + translation: "целевое значение не равно пороговому значению" + +- id: "ErrInvalidThreshold" + translation: "пороговое значение недействительно" + +- id: "ErrNotEqual" + translation: "целевое значение равно пороговому значению" + +- id: "ErrGreaterThan" + translation: "целевое значение не больше порогового значения" + +- id: "ErrGreaterThanEqual" + translation: "целевое значение не больше или равно пороговому значению" + +- id: "ErrLessThan" + translation: "целевое значение не меньше порогового значения" + +- id: "ErrLessThanEqual" + translation: "целевое значение не меньше или равно пороговому значению" + +- id: "ErrMin" + translation: "целевое значение меньше минимального значения" + +- id: "ErrMax" + translation: "целевое значение больше максимального значения" + +- id: "ErrLength" + translation: "длина целевого значения не равна пороговому значению" + +- id: "ErrOneOf" + translation: "целевое значение не является одним из допустимых значений" + +- id: "ErrLoadMessageFile" + translation: "не удалось загрузить файл сообщения" diff --git a/option.go b/option.go index fe913e0..57f49a8 100644 --- a/option.go +++ b/option.go @@ -30,3 +30,11 @@ func WithJapaneseLanguage() Option { return nil } } + +// WithRussianLanguage is an Option that sets the i18n bundle to Russian. +func WithRussianLanguage() Option { + return func(c *CSV) error { + c.i18nLocalizer = i18n.NewLocalizer(c.i18nBundle, "ru") + return nil + } +}