Skip to content

16 - i18n DateTime

EF Changes

In model classes, use attributes
- DataType(DataType.DateTime) - DataType(DataType.Date) - DataType(DataType.Time)

1
2
3
4
5
6
7
8
[DataType(DataType.DateTime)] 
public DateTime DateTime { get; set; } 

[DataType(DataType.Date)] 
public DateTime Date { get; set; } 

[DataType(DataType.Time)] 
public DateTime Time { get; set; }
1
2
3
4
5
6
7
8
<input 
    class="form-control" 
    type="datetime" 
    data-val="true" 
    data-val-required="The DateAndTime field is required." 
    id="FooBar_DateAndTime" 
    name="FooBar.DateAndTime" 
    value="" />

Browsers 1

  • Chrome/Edge adds html5 support
  • Can't submit, will usually not validate on serverside (format mismatch between server and browser)

AM

AM

Browsers 2

Firefox – Dropdown on date (US)
Time and DateTime - nothing

Unobtrusive (jquery) validation also fails

AM

Browser solutions

  • Use simple text fields and dropdowns, skip clientside datetime validation
    • Can be problematic, different regions are used to their own way of seeing culture support (money, time, commas, etc)
  • Use js plugins to generate html and validate inputs – jquery
    • Hard to setup correctly
      • Jquery unobtrusive validate initially supports only en-US
      • Several jquery/bootstrap/standalone plugins to generate html inputs for datetime types

JS

  • Using bower/npm/libman to setup js build mechanism – lots of manual configuring, hard to maintain
  • Implement full JS frontend build pipeline with WebPack – hard to setup, later much easier


  • CLDR – specs for all languages/regions – numbers, dates, etc
    • http://cldr.unicode.org
  • Globalize – js lib for i18n
    • https://github.com/globalizejs/globalize

webpack.config.js

  • Entry points
  • Different loaders – ts, css, images, fonts


  • Files to look for
  • Output files
  • Plugin
    • File copy
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
const path = require('path');
const webpack = require('webpack');
// const HtmlPlugin = require('html-webpack-plugin');
const CopyPlugin = require("copy-webpack-plugin");
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserPlugin = require("terser-webpack-plugin");

module.exports = {
    mode: 'production',
    entry: {
        site: "./src/site.ts",
        'jquery.validate.globalize': './src/jquery.validate.globalize.js',
        'subPrograms': './src/subPrograms.js',
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: "[name].js",
        publicPath: '',
    },
    optimization: {
        minimize: true,
        minimizer: [
            new TerserPlugin({
                extractComments: false,
            }),
        ],
    },
    module: {
        rules: [{
                test: /\.tsx?$/,
                use: 'ts-loader',
                exclude: /node_modules/,
            },
            {
                test: /\.css$/i,
                use: [MiniCssExtractPlugin.loader, 'css-loader'],
            },
            {
                test: /\.exec\.js$/,
                use: 'script-loader'
            },
            {
                test: /\.(png|jpg|gif)$/i,
                use: [{
                    loader: 'url-loader',
                    options: {
                        limit: 10240,
                    },
                }, ],
            },
            {
                test: /\.woff(\?v=[0-9]\.[0-9]\.[0-9])?$/i,
                use: [{
                    loader: 'url-loader',
                    options: {
                        limit: 10240,
                        mimetype: 'application/font-woff'
                    },
                }, ],
            },
            {
                test: /\.woff2(\?v=[0-9]\.[0-9]\.[0-9])?$/i,
                use: [{
                    loader: 'url-loader',
                    options: {
                        limit: 10240,
                        mimetype: 'application/font-woff2'
                    },
                }, ],
            },

            {
                test: /\.ttf(\?v=[0-9]\.[0-9]\.[0-9])?$/i,
                use: [{
                    loader: 'url-loader',
                    options: {
                        limit: 10240,
                        mimetype: 'application/octet-stream'
                    },
                }, ],
            },
            {
                test: /\.svg(\?v=[0-9]\.[0-9]\.[0-9])?$/i,
                use: [{
                    loader: 'url-loader',
                    options: {
                        limit: 10240,
                        mimetype: 'image/svg+xml'
                    },
                }, ],
            }, {
                test: /\.(eot|otf)(\?v=[0-9]\.[0-9]\.[0-9])?$/i,
                use: [{
                    loader: 'file-loader',
                    options: {
                        esModule: false
                    },
                }, ],
            },

        ],
    },
    resolve: {
        extensions: ['.tsx', '.ts', '.js'],
        alias: {
            'cldr$': 'cldrjs',
            'cldr': 'cldrjs/dist/cldr'
        },
    },
    plugins: [
        /*
        new HtmlPlugin({
            template: "./src/index.html",
            inject: "body",
            minify: false,
        }),
        */
        new MiniCssExtractPlugin(),
        new CleanWebpackPlugin(),
        new CopyPlugin({
            patterns: [
                { from: 'node_modules/cldr-core/supplemental/likelySubtags.json', to: 'cldr-core/supplemental' },
                { from: 'node_modules/cldr-core/supplemental/numberingSystems.json', to: 'cldr-core/supplemental' },
                { from: 'node_modules/cldr-core/supplemental/timeData.json', to: 'cldr-core/supplemental' },
                { from: 'node_modules/cldr-core/supplemental/weekData.json', to: 'cldr-core/supplemental' },

                { from: 'node_modules/cldr-numbers-modern/main/et/', to: 'cldr-numbers-modern/main/et/' },
                { from: 'node_modules/cldr-dates-modern/main/et/', to: 'cldr-dates-modern/main/et/' },

                { from: 'node_modules/cldr-numbers-modern/main/en-GB/', to: 'cldr-numbers-modern/main/en/' },
                { from: 'node_modules/cldr-dates-modern/main/en-GB/', to: 'cldr-dates-modern/main/en/' },

                { from: 'node_modules/cldr-numbers-modern/main/ru/', to: 'cldr-numbers-modern/main/ru/' },
                { from: 'node_modules/cldr-dates-modern/main/ru/', to: 'cldr-dates-modern/main/ru/' },
            ]
        }),

    ]
}

site.ts

  • script-loader!...
    • Load all the js libs globally
  • Import all the css etc
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
console.log("JS Startup");

import 'script-loader!jquery';
import 'script-loader!jquery-validation/dist/jquery.validate.min';
import 'script-loader!jquery-validation-unobtrusive/dist/jquery.validate.unobtrusive.min';

import 'script-loader!bootstrap/dist/js/bootstrap.bundle.min.js';

import 'script-loader!cldrjs/dist/cldr.js';
import 'script-loader!cldrjs/dist/cldr/event.js';
import 'script-loader!cldrjs/dist/cldr/supplemental.js';
import 'script-loader!cldrjs/dist/cldr/unresolved.js';

import 'script-loader!globalize/dist/globalize.js';

import 'script-loader!globalize/dist/globalize/number.js';
import 'script-loader!globalize/dist/globalize/currency.js';
import 'script-loader!globalize/dist/globalize/date.js';
import 'script-loader!globalize/dist/globalize/message.js';
import 'script-loader!globalize/dist/globalize/plural.js';
import 'script-loader!globalize/dist/globalize/relative-time.js';
import 'script-loader!globalize/dist/globalize/unit.js';

import 'script-loader!flatpickr/dist/flatpickr.min';
import 'script-loader!flatpickr/dist/l10n/ru.js';
import 'script-loader!flatpickr/dist/l10n/et.js';

import 'script-loader!autocompleter/autocomplete.min.js';
import 'autocompleter/autocomplete.min.css';

// import 'bootstrap/dist/css/bootstrap.min.css';
import "bootswatch/dist/superhero/bootstrap.min.css";
import 'font-awesome/css/font-awesome.min.css';
import 'flatpickr/dist/flatpickr.min.css';
import './main.css'

import './pagedlist.css'

globalize.js in asp.net

  • Set up i18n for clientside js in your templates.
  • Replace html5 date/time fields with regular textfields
  • Attach some js lib for date/time unified UX in all browsers
  • Use globalize.js in jquery-validation

js in asp template

1
2
<script src="~/js/site.js" asp-append-version="true"></script>
<script src="~/js/jquery.validate.globalize.js" asp-append-version="true"></script>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@{    
    var currentCultureCode = Thread.CurrentThread.CurrentCulture.Name.Split('-')[0];

    // map .net datetime format strings to flatpick/momentjs format

    // https://flatpickr.js.org/formatting/
    // d - day of month,2 digits
    // j - day of month, no leading zero
    // m - month, 2 digits
    // n - mont, no leading zero
    // y - 2 digit year, Y - 4 digit year

    // https://docs.microsoft.com/en-us/dotnet/api/system.globalization.datetimeformatinfo?view=netcore-3.1
    // dd.MM.yyyy or dd/MM/yyyy

    var datePattern = Thread.CurrentThread.CurrentUICulture.DateTimeFormat.ShortDatePattern;
    datePattern = datePattern
        .Replace("dd", "d")
        .Replace("MM", "m")
        .Replace("yyyy", "Y");

    // LongTimePattern and ShortTimePattern HH:mm for 23:59,  h:mm tt for 11:59 PM
    var timePattern = Thread.CurrentThread.CurrentUICulture.DateTimeFormat.ShortTimePattern;
    var clock24H = timePattern.Contains("tt") == false;
    timePattern = timePattern
        .Replace("HH", "H")
        .Replace("mm", "i")
        .Replace("ss", "S")
        .Replace("tt", "K");
    var dateTimePattern = timePattern + " " + datePattern;
}

set up globalize in template

  • Load the cldr data for current locale
  • Initialize Globalize to use cldr data
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<script>
    // https://github.com/globalizejs/globalize#installation
    $.when(
        $.get("/js/cldr-core/supplemental/likelySubtags.json", null, null, "json"),
        $.get("/js/cldr-core/supplemental/numberingSystems.json", null, null, "json"),
        $.get("/js/cldr-core/supplemental/timeData.json", null, null, "json"),
        $.get("/js/cldr-core/supplemental/weekData.json", null, null, "json"),

        $.get("/js/cldr-numbers-modern/main/@currentCultureCode/numbers.json", null, null, "json"),
        $.get("/js/cldr-numbers-modern/main/@currentCultureCode/currencies.json", null, null, "json"),

        $.get("/js/cldr-dates-modern/main/@currentCultureCode/ca-generic.json", null, null, "json"),
        $.get("/js/cldr-dates-modern/main/@currentCultureCode/ca-gregorian.json", null, null, "json"),
        $.get("/js/cldr-dates-modern/main/@currentCultureCode/dateFields.json", null, null, "json"),
        $.get("/js/cldr-dates-modern/main/@currentCultureCode/timeZoneNames.json", null, null, "json")
    ).then(function () {
        return [].slice.apply(arguments, [0]).map(function (result) {
            Globalize.load(result[0]);
        });
    }).then(function () {
        // Initialise Globalize to the current culture
        Globalize.locale('@currentCultureCode');
    });
</script>

add js plugin for datetime fields

Replace browser html5 inputs with custom js library - flatpickr here

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<script>
    $(function () {
        $('[type="datetime-local"]').each(function (index, value) {
            $(value).attr('type', 'text');
            $(value).val(value.defaultValue);
            $(value).flatpickr({
                locale: "@currentCultureCode",
                enableTime: true,
                altFormat: "@dateTimePattern",
                altInput: true,
                // dateFormat: "Z", // iso format (causes -3h during summer)
                // use direct conversion, let backend deal with utc/whatever conversions
                dateFormat: "Y-m-d H:i:s",
                disableMobile: true,
                time_24hr: @(clock24H.ToString().ToLower()),
            });
        });

        $('[type="time"]').each(function (index, value) {
            $(value).attr('type', 'text');
            $(value).val(value.defaultValue);
            $(value).flatpickr({
                locale: "@currentCultureCode",
                enableTime: true,
                noCalendar: true,

                altFormat: "@timePattern",
                altInput: true,
                dateFormat: "H:i", // 24h HH:mm
                disableMobile: true,

                time_24hr: @(clock24H.ToString().ToLower()),
            });
        });

        $('[type="date"]').each(function (index, value) {
            $(value).attr('type', 'text');
            $(value).val(value.defaultValue);
            $(value).flatpickr({
                locale: "@currentCultureCode",
                altFormat: "@datePattern",
                altInput: true,
                disableMobile: true,
                dateFormat: "Y-m-d", // YYYY-MM-DD
            });
        });
    });
</script>

replace validation functionality in jquery

jquery.validate.globalize.js

Include it after other jquery scripts

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/*!
** An extension to the jQuery Validation Plugin which makes it use Globalize.js for number and date parsing
** by Andres Käver, based on work by John Reilly
*/

(function ($, Globalize) {

    // Clone original methods we want to call into
    var originalMethods = {
        min: $.validator.methods.min,
        max: $.validator.methods.max,
        range: $.validator.methods.range
    };

    // Globalize options
    // Users can customise this to suit them
    // https://github.com/jquery/globalize/blob/master/doc/api/date/date-formatter.md
    $.validator.methods.dateGlobalizeOptions = { dateParseFormat: [{ skeleton: "yMd" }, { skeleton: "yMMMd" }, { date: "short" }, { date: "medium" }, { date: "long" }, { date: "full" }] };
    $.validator.methods.timeGlobalizeOptions = { dateParseFormat: [{ skeleton: "Hm" }, { skeleton: "hm" }, { time: "short" }, { time: "medium" }, { time: "long" }, { time: "full" }] };
    $.validator.methods.datetimeGlobalizeOptions = {
        dateParseFormat: [{ skeleton: "yMdHm" }, { skeleton: "yMdhm" }, { datetime: "short" }, { datetime: "medium" }, { datetime: "long" }, { datetime: "full" },
        { raw: "d.M.y H:m" }, { raw: "dd/MM/y HH:mm" }]
    };


    // Tell the validator that we want dates parsed using Globalize
    $.validator.methods.date = function (value, element) {
        // is it optional
        if (this.optional(element) === true) return true;

        //TODO: this is an hack
        if ($(element).attr("type") === "datetime") return true;

        // remove spaces just in case
        value = value.trim();
        var res = false;
        var val;
        // console.log("date validation: ", value);
        // console.log(element);
        for (var i = 0; i < $.validator.methods.dateGlobalizeOptions.dateParseFormat.length; i++) {
            val = Globalize.parseDate(value, $.validator.methods.dateGlobalizeOptions.dateParseFormat[i]);
            // console.log($.validator.methods.dateGlobalizeOptions.dateParseFormat[i], val, Globalize.dateFormatter($.validator.methods.dateGlobalizeOptions.dateParseFormat[i])(new Date(2016, 1, 1, 0, 0, 0)));
            res = res || (val instanceof Date);
            // console.log(res);
            if (res === true) return res;
        }
        return res;
    };

    // additional method
    $.validator.methods.time = function (value, element) {
        // is it optional
        if (this.optional(element) === true) return true;

        // remove spaces just in case
        value = value.trim();
        var res = false;
        var val;

        // console.log("time validation: ", value);
        // console.log(element);
        for (var i = 0; i < $.validator.methods.timeGlobalizeOptions.dateParseFormat.length; i++) {
            val = Globalize.parseDate(value, $.validator.methods.timeGlobalizeOptions.dateParseFormat[i]);
            console.log($.validator.methods.timeGlobalizeOptions.dateParseFormat[i], val, Globalize.dateFormatter($.validator.methods.timeGlobalizeOptions.dateParseFormat[i])(new Date(2016, 1, 1, 0, 0, 0)));
            res = res || (val instanceof Date);
            console.log(res);
            if (res === true) return res;
        }
        return res;
    };

    // additional method
    $.validator.methods.datetime = function (value, element) {
        // is it optional
        if (this.optional(element) === true) return true;

        // remove spaces just in case
        value = value.trim();
        var res = false;
        var val;

        // console.log("datetime validation: ", value);
        // console.log(element);
        for (var i = 0; i < $.validator.methods.datetimeGlobalizeOptions.dateParseFormat.length; i++) {
            val = Globalize.parseDate(value, $.validator.methods.datetimeGlobalizeOptions.dateParseFormat[i]);
            // console.log($.validator.methods.datetimeGlobalizeOptions.dateParseFormat[i], val, Globalize.dateFormatter($.validator.methods.datetimeGlobalizeOptions.dateParseFormat[i])(new Date(2016, 1, 1, 1, 1, 1)));
            res = res || (val instanceof Date);
            // console.log(res);
            if (res === true) return res;
        }
        return res;
    };

    // Tell the validator that we want numbers parsed using Globalize
    $.validator.methods.number = function (value, element) {
        var val = Globalize.parseNumber(value.replace(".", ","));
        if (!isFinite(val)){
            val = Globalize.parseNumber(value.replace(",", "."));
        }
        var res = this.optional(element) || isFinite(val);
        return res;
    };

    // Tell the validator that we want numbers parsed using Globalize,
    // then call into original implementation with parsed value

    $.validator.methods.min = function (value, element, param) {
        var val = Globalize.parseNumber(value);
        return originalMethods.min.call(this, val, element, param);
    };

    $.validator.methods.max = function (value, element, param) {
        var val = Globalize.parseNumber(value);
        return originalMethods.max.call(this, val, element, param);
    };

    $.validator.methods.range = function (value, element, param) {
        var val = Globalize.parseNumber(value);
        return originalMethods.range.call(this, val, element, param);
    };

    //create adapters for new type - so they will be attached automatically
    //this depends on attribute data-val-time, data-val-datetime

    $.validator.unobtrusive.adapters.addBool('time');
    $.validator.unobtrusive.adapters.addBool('datetime');

}(jQuery, Globalize));

., in decimals

Add custom model binder – replace the default floating point binder. Allow both “.” and “,” as decimal separator. (what about thousands separator)...

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace WebApp.Helpers
{
    public class CustomFloatingPointBinderProvider : IModelBinderProvider
    {
        public IModelBinder? GetBinder(ModelBinderProviderContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (context.Metadata.ModelType == typeof(decimal) ||
                context.Metadata.ModelType == typeof(float) ||
                context.Metadata.ModelType == typeof(double))
            {
                var loggerFactory = context.Services.GetRequiredService<ILoggerFactory>();
                return new FloatingPointModelBinder(loggerFactory, context.Metadata.ModelType);
            }

            return null;
        }
    }


    public class FloatingPointModelBinder : IModelBinder
    {
        private readonly ILogger<FloatingPointModelBinder>? _logger;
        private readonly Type _floatType;

        public FloatingPointModelBinder(ILoggerFactory? loggerFactory, Type floatType)
        {
            _logger = loggerFactory?.CreateLogger<FloatingPointModelBinder>();
            _floatType = floatType;
        }

        public Task BindModelAsync(ModelBindingContext bindingContext)
        {
            if (bindingContext == null)
            {
                throw new ArgumentNullException(nameof(bindingContext));
            }

            var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);

            if (valueProviderResult == ValueProviderResult.None)
            {
                return Task.CompletedTask;
            }

            var value = valueProviderResult.FirstValue;

            if (string.IsNullOrEmpty(value))
            {
                return Task.CompletedTask;
            }

            // Remove unnecessary commas and spaces
            //value = value.Replace(",", string.Empty).Trim();

            _logger?.LogDebug($"Floating point number: {value}");
            value = value.Trim();

            value = value.Replace(",", Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator);
            value = value.Replace(".", Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator);

            if (_floatType == typeof(decimal))
            {
                if (!decimal.TryParse(value, out var resultValue))
                {
                    bindingContext.ModelState.TryAddModelError(
                        bindingContext.ModelName,
                        $"Could not parse decimal {value}.");
                    return Task.CompletedTask;
                }

                bindingContext.Result = ModelBindingResult.Success(resultValue);
            }
            else if (_floatType == typeof(float))
            {
                if (!float.TryParse(value, out var resultValue))
                {
                    bindingContext.ModelState.TryAddModelError(
                        bindingContext.ModelName,
                        $"Could not parse float {value}.");
                    return Task.CompletedTask;
                }

                bindingContext.Result = ModelBindingResult.Success(resultValue);
            }
            else if (_floatType == typeof(double))
            {
                if (!double.TryParse(value, out var resultValue))
                {
                    bindingContext.ModelState.TryAddModelError(
                        bindingContext.ModelName,
                        $"Could not parse double {value}.");
                    return Task.CompletedTask;
                }

                bindingContext.Result = ModelBindingResult.Success(resultValue);
            }

            return Task.CompletedTask;
        }
    }
}

Plug factory into MVC

1
2
3
4
5
services
    .AddControllersWithViews(options =>
    {
        options.ModelBinderProviders.Insert(0, new CustomFloatingPointBinderProvider());
    })

Model binding inner translations

1
builder.Services.AddSingleton<IConfigureOptions<MvcOptions>, ConfigureModelBindingLocalization>();
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
public class ConfigureModelBindingLocalization : IConfigureOptions<MvcOptions>
{
    private readonly IServiceScopeFactory _serviceFactory;

    public ConfigureModelBindingLocalization(IServiceScopeFactory serviceFactory)
    {
        _serviceFactory = serviceFactory;
    }

    public void Configure(MvcOptions options)
    {

        options.ModelBindingMessageProvider.SetAttemptedValueIsInvalidAccessor((x, y) =>
            string.Format(Resources.Base.Common.ErrorMessage_AttemptedValueIsInvalid, x, y));

        options.ModelBindingMessageProvider.SetMissingBindRequiredValueAccessor((x) =>
            string.Format(Resources.Base.Common.ErrorMessage_MissingBindRequiredValue, x));

        // localizer["A value for the '{0}' parameter or property was not provided.", x]);

        options.ModelBindingMessageProvider.SetMissingKeyOrValueAccessor(() =>
            Resources.Base.Common.ErrorMessage_MissingKeyOrValue);

        // localizer["A value is required."]);

        options.ModelBindingMessageProvider.SetMissingRequestBodyRequiredValueAccessor(() =>
            Resources.Base.Common.ErrorMessage_MissingRequestBodyRequiredValue);

        // localizer["A non-empty request body is required."]);

        options.ModelBindingMessageProvider.SetNonPropertyAttemptedValueIsInvalidAccessor((x) =>
            string.Format(Resources.Base.Common.ErrorMessage_NonPropertyAttemptedValueIsInvalid, x));
        // localizer["The value '{0}' is not valid.", x]);

        options.ModelBindingMessageProvider.SetNonPropertyUnknownValueIsInvalidAccessor(() =>
            Resources.Base.Common.ErrorMessage_NonPropertyUnknownValueIsInvalid);
        // localizer["The supplied value is invalid."]);

        options.ModelBindingMessageProvider.SetNonPropertyValueMustBeANumberAccessor(() =>
            Resources.Base.Common.ErrorMessage_NonPropertyValueMustBeANumber);
        // localizer["The field must be a number."]);

        options.ModelBindingMessageProvider.SetUnknownValueIsInvalidAccessor((x) =>
            string.Format(Resources.Base.Common.ErrorMessage_UnknownValueIsInvalid, x));
        //  localizer["The supplied value is invalid for {0}.", x]);

        options.ModelBindingMessageProvider.SetValueIsInvalidAccessor((x) =>
            string.Format(Resources.Base.Common.ErrorMessage_ValueIsInvalid, x));
        //  localizer["The value '{0}' is invalid.", x]);

        options.ModelBindingMessageProvider.SetValueMustBeANumberAccessor((x) =>
            string.Format(Resources.Base.Common.ErrorMessage_ValueMustBeANumber, x));
        //  localizer["The field {0} must be a number.", x]);

        options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor((x) =>
            string.Format(Resources.Base.Common.ErrorMessage_ValueMustNotBeNull, x));
        //  localizer["The value '{0}' is invalid.", x]);
    }
}