Γράφοντας ένα πρόγραμμα ανάγνωσης πόρων json στο .NET Core

Θα δημιουργήσουμε ένα προσαρμοσμένο πρόγραμμα ανάγνωσης πόρων για χρήση με βιβλιοθήκες .NET Core.

Category Fundamentals

Published: 31 Ιουλίου 2021

Σε αυτήν την ανάρτηση, θα δημιουργήσουμε ένα δικό μας πρόγραμμα ανάγνωσης πόρων για χρήση με βιβλιοθήκες .NET Core. Στο τέλος, θα  έχουμε ένα project αφιερωμένο στους πόρους.

Αρχικώς, χρειαζόμαστε μια κλάση που να αντιπροσωπεύει τον φάκελό json μας όπως παρακάτω:

using System.Collections.Generic;

namespace I18N
{
    internal class JsonLocalization
    {
        public string Key { getset; }
        public Dictionary<string, string> LocalizedValues { getset; }
    }
}
 

Το Key είναι ένα μοναδικό αναγνωριστικό για την τοπική προσαρμογή και το LocalizedValues είναι ένα λεξικό, του οποίου το κλειδί του είναι η γλώσσα και η τιμή του το κειμένου που πρέπει να εμφανίζεται.

Θα δημιουργήσουμε επίσης ένα νέο τύπο Exception, για να εντοπίζουμε εύκολα τι πήγε στραβά κατά την εκτέλεση της εφαρμογής.

using System;

namespace I18N
{
    public class I18NException : Exception
    {
        public I18NException(string message: base(message)
        {
        }

        public I18NException(string message, Exception innerException: base(message, innerException)
        {
        }

        public I18NException()
        {
        }
    }
}
 

Και εδώ συμβαίνει η μαγεία, η κλάση JsonLocalizer θα μας διαβάσει τα json resources files, θα τα αποθηκεύσει στη μνήμη και θα τα διαθέσει στην εφαρμογή μας.

Στον κατασκευαστή μας αναμένουμε δύο παραμέτρους, useBase και additionalPaths.

Εάν η τιμή useBase έχει οριστεί σε αληθές, ο τοπικοποιητής θα φορτώσει τα αρχεία *.json που βρίσκονται στο κατάλογο Resources. Το additionalPaths χρησιμοποιεί έναν τύπο ως κλειδί, ο τοποποιητής θα χρησιμοποιήσει αυτόν τον τύπο για να βρει τη διαδρομή συγκρότησης και να διαβάσει τα αρχεία *.json μέσα από τον κατάλογο Resources.

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using Newtonsoft.Json;

namespace I18N
{
    public class JsonLocalizer
    {
        private readonly Dictionary<string, JsonLocalization[]> _localization
            = new Dictionary<string, JsonLocalization[]>();

        public JsonLocalizer(bool useBase = true, Dictionary<Type, stringadditionalPaths = null)
        {
            if (useBase)
                PopulateLocalization("Resources");

            if (additionalPaths == nullreturn;
            foreach (var additional in additionalPaths)
            {
                var codeBase = additional.Key.Assembly.CodeBase;
                var uri = new UriBuilder(codeBase);
                var data = Uri.UnescapeDataString(uri.Path);
                var path = Path.GetDirectoryName(data);
                var fullPath = Path.Combine(path, additional.Value);
                PopulateLocalization(fullPath);
            }
        }

        /// <summary>
        /// resource:key:culture
        /// resource is the resource name
        /// key is the key you're looking for
        /// culture is optional
        /// </summary>
        /// <param name="key"></param>
        public string this[string key=> GetString(key);


        private void PopulateLocalization(string path)
        {
            foreach (var resource in Directory.GetFiles(path, "*.json", SearchOption.AllDirectories))
            {
                try
                {
                    var fileInfo = new FileInfo(resource);
                    var fileName = fileInfo.Name.Substring(0, fileInfo.Name.IndexOf('.'));
                    var loc = JsonConvert.DeserializeObject<JsonLocalization[]>(File.ReadAllText(resource));
                    _localization.Add(fileName, loc);
                }
                catch (ArgumentException e)
                {
                    throw new I18NException($"Resource {resource} was already added, check your files.", e);
                }
                catch (Exception ex)
                {
                    throw new I18NException("Something wrong is not right, check inner exception", ex);
                }
            }
        }

        private string GetString(string query)
        {
            try
            {
                string culture = null;

                var split = query.Split(':');
                var resource = split[0];
                var key = split[1];
                if (split.Length > 2)
                    culture = split[2];

                culture = culture ?? CultureInfo.CurrentCulture.Name;

                return _localization
                    .Single(l => l.Key == resource)
                    .Value.Single(x => x.Key == key)
                    .LocalizedValues[culture];
            }
            catch (Exception ex)
            {
                throw new I18NException($"Couldn't find key: {query}", ex);
            }
        }
    }
}
 

Στις εφαρμογές dotnet core, μπορείτε να προσθέσετε το JsonLocalizer με την χρήση της μεθόδου IServiceCollection μέσα στο ConfigureServices.

// use it in DI as a singleton
public void ConfigureServices(IServiceCollection services)
{
   // Other configurations ...
   services.AddSingleton<JsonLocalizer>();
}
 

Για να χειριστείτε additionalPath

var additional = new Dictionary<Type, string>
    {
        { typeof(MyClass), "My Resource Folder" },
        { typeof(MyAnotherClass), "My Resource Folder/Even Handles sub folders" }
    };

var withExternalSources = new JsonLocalizer(additionalPaths: additional);
 
 

Τώρα που τα έχουμε όλα στημένα, μπορούμε να αρχίσουμε να χρησιμοποιούμε τον τοπικοποιητή μας:

private readonly JsonLocalizer _localizer;

public class MySampleClass(JsonLocalizer localizer)
{
   _localizer = localizer;
}

public string GetLocalizedMessage()
{
   return _localizer["MyAppResource:MyKey"];
}
 
 

Ο τοπικοποιητής θα βρει το κείμενό σας με:

FileName:Key:Language

Ακολουθούν ορισμένα παραδείγματα για το πώς μπορείτε να γράψετε τα αρχεία πόρων σας:

Όνομα αρχείουΌνομα πόρου
MyResource.jsonMyResource
MyApp.Resource.jsonMyApp
MyApp-Errors.Resource.jsonMyApp-Errors
MyApp.Errors.Resource.jsonMyApp
 


Το Key είναι το κλειδί μέσα στο αρχείο πόρων και η Language είναι η κουλτούρα. Εάν δεν ενημερωθεί, θα χρησιμοποιήσει την τιμή του CultureInfo.CurrentCulture.

Το αρχείο πόρων json θα πρέπει να ακολουθεί την παρακάτω κατάλληλη μορφή:

[
    {
        "Key": "Name",
        "LocalizedValues": {
            "en-US": "Name",
            "pt-BR": "Nome"
        }
    },
    {
        "Key": "Age",
        "LocalizedValues": {
            "en-US": "Age",
            "pt-BR": "Idade"
        }
    }
]