ডিসপ্লেনামএট্রিবিউটের স্থানীয়করণ


120

আমি একটি প্রপার্টিগ্রিডে প্রদর্শিত বৈশিষ্ট্যের নাম স্থানীয়করণের একটি উপায় খুঁজছি। ডিসপ্লে-নেমঅ্যাট্রিবিউট অ্যাট্রিবিউট ব্যবহার করে সম্পত্তিটির নাম "ওভাররাইডেন" হতে পারে। দুর্ভাগ্যক্রমে গুণাবলী অ ধ্রুবক প্রকাশ করতে পারে না। সুতরাং আমি দৃ strongly়ভাবে টাইপ করা সংস্থান যেমন ব্যবহার করতে পারি না:

class Foo
{
   [DisplayAttribute(Resources.MyPropertyNameLocalized)]  // do not compile
   string MyProperty {get; set;}
}

আমার চারপাশে এক নজর ছিল এবং উত্স ব্যবহার করতে সক্ষম হতে ডিসপ্লেনামএট্রিবিউট থেকে উত্তরাধিকার সূত্রে কিছু পরামর্শ পেয়েছি। আমি কোড সহ শেষ করব:

class Foo
{
   [MyLocalizedDisplayAttribute("MyPropertyNameLocalized")] // not strongly typed
   string MyProperty {get; set;}
}

তবে আমি দৃ strongly়ভাবে টাইপ করা সংস্থান সুবিধাগুলি হারাচ্ছি যা অবশ্যই ভাল জিনিস নয়। তারপরে আমি ডিসপ্লে-নেম রিসোর্সঅ্যাট্রিবিউট জুড়ে এসেছিলাম যা আমি যা খুঁজছি তা হতে পারে। তবে এটি মাইক্রোসফ্টে থাকার কথা। ভিজ্যুয়াল স্টুডিও od মডেলিং name ডিজাইন নেমস্পেস এবং আমি এই নেমস্পেসের জন্য আমার কোন রেফারেন্স যুক্ত করতে হবে তা খুঁজে পাচ্ছি না।

কেউ কি জানেন যে কোনও ভাল উপায়ে ডিসপ্লেনেম স্থানীয়করণ অর্জনের আরও সহজ উপায় আছে কিনা? বা মাইক্রোসফ্ট ভিজ্যুয়াল স্টুডিওর জন্য ব্যবহার করছে বলে মনে করার মতো উপায় আছে?


2
কি প্রদর্শন করি সেই সম্পর্কে (ResourceType = typeof (ResourceStrings), নাম = "MyProperty") দেখতে msdn.microsoft.com/en-us/library/...
পিটার

@ পিটার পোস্টটি মনোযোগ সহকারে পড়ুন, তিনি ঠিক বিপরীতে চান, রিসোর্সস্ট্রিংস ব্যবহার করে এবং সঠিক কোডিং স্ট্রিং না করে টাইম চেক সংকলন করুন ...
মার্কো

উত্তর:


113

নেই ডিসপ্লে অ্যাট্রিবিউট System.ComponentModel.DataAnnotations থেকে .NET 4. মধ্যে এটা MVC 3 উপর কাজ করে PropertyGrid

[Display(ResourceType = typeof(MyResources), Name = "UserName")]
public string UserName { get; set; }

এটি UserNameআপনার MyResources.resx ফাইলে নামের একটি সংস্থান দেখায় ।


এই পৃষ্ঠাটি সন্ধান করার আগে আমি সমস্ত দিকে তাকিয়ে ছিলাম ... এটি এমন একটি জীবনরক্ষক। ধন্যবাদ! আমার জন্য এমভিসি 5 তে ভাল কাজ করে।
ক্রিস

কম্পাইলার যদি অভিযোগ করে থাকে তবে typeof(MyResources)আপনার রিসোর্স ফাইল অ্যাক্সেস সংশোধকটিকে সর্বজনীনতে সেট করতে হবে ।
জ্ঞানীগুই

80

আমরা একাধিক ভাষা সমর্থন করার জন্য কয়েকটি গুণাবলীর জন্য এটি করছি। আমরা মাইক্রোসফ্টে অনুরূপ পন্থা নিয়েছি, যেখানে তারা তাদের মূল বৈশিষ্ট্যগুলি ওভাররাইড করে এবং প্রকৃত স্ট্রিংয়ের পরিবর্তে কোনও সংস্থান নাম দেয়। রিসোর্সের নামটি আসল স্ট্রিংয়ের ফেরতের জন্য ডিএলএল সংস্থানগুলিতে একটি অনুসন্ধান সম্পাদন করতে ব্যবহৃত হয়।

উদাহরণ স্বরূপ:

class LocalizedDisplayNameAttribute : DisplayNameAttribute
{
    private readonly string resourceName;
    public LocalizedDisplayNameAttribute(string resourceName)
        : base()
    {
      this.resourceName = resourceName;
    }

    public override string DisplayName
    {
        get
        {
            return Resources.ResourceManager.GetString(this.resourceName);
        }
    }
}

প্রকৃতপক্ষে বৈশিষ্ট্যটি ব্যবহার করার সময় আপনি এটিকে আরও একধাপ এগিয়ে নিয়ে যেতে পারেন এবং স্থিত শ্রেণীর স্থায়ী হিসাবে আপনার সংস্থানগুলির নাম উল্লেখ করতে পারেন। এইভাবে, আপনি যেমন ঘোষণা পেতে।

[LocalizedDisplayName(ResourceStrings.MyPropertyName)]
public string MyProperty
{
  get
  {
    ...
  }
}

আপডেটের
ResourceStrings মতো দেখতে কিছু হবে (দ্রষ্টব্য, প্রতিটি স্ট্রিং এমন একটি সংস্থার নামকে বোঝায় যা প্রকৃত স্ট্রিং নির্দিষ্ট করে):

public static class ResourceStrings
{
    public const string ForegroundColorDisplayName="ForegroundColorDisplayName";
    public const string FontSizeDisplayName="FontSizeDisplayName";
}

আমি যখন এই পদ্ধতির চেষ্টা করি তখন আমি একটি ত্রুটি বার্তা পাই যা বলে "একটি অ্যাট্রিবিউট আর্গুমেন্ট অবশ্যই একটি ধ্রুবক অভিব্যক্তি, টাইপফ এক্সপ্রেশন বা কোনও অ্যাট্রিবিউট প্যারামিটার ধরণের অ্যারে তৈরির এক্সপ্রেশন হতে হবে"। তবে, স্ট্রিং হিসাবে কাজ করে লোকালাইজডডিস্পলনামে মান পাস করা। ইচ্ছে করে এটি শক্তভাবে টাইপ করা হবে।
আজুর এসএমই

1
@ অ্যান্ডি: রিসোর্সস্ট্রিংসের মানগুলি অবশ্যই ধ্রুবক হতে হবে, যেমন উত্তরে বর্ণিত হয়েছে, সম্পত্তি বা পঠনযোগ্য মান নয়। এগুলি অবশ্যই কনস্ট হিসাবে চিহ্নিত করা উচিত এবং সংস্থানগুলির নাম উল্লেখ করুন, অন্যথায় আপনি একটি ত্রুটি পাবেন।
জেফ ইয়েটস

1
আমার নিজের প্রশ্নের উত্তর দিয়েছিলেন, আপনার কোথায় ছিল রিসোর্সস. রিসোর্স ম্যানেজার, আমার ক্ষেত্রে রেক্স ফাইলগুলি সর্বজনীন রেসেক্স তৈরি করা হয়েছিল তাই এটি হয়েছিল[MyNamespace].[MyResourceFile].ResourceManager.GetString("MyString");
ত্রিস্তান ওয়ার্নার-স্মিথ

বলেছে যে এটিতে স্ট্রিং পেতে কল করার জন্য আমার রিসোর্স
ম্যানেজারের

1
@ এলটিআর: কোন সমস্যা নেই। আপনি সন্তুষ্ট হয়েছি যে আপনি সমস্যার তলটিতে এসেছেন। সাহায্য করতে পেরে খুশি।
জেফ ইয়েটস

41

এখানে পৃথক সমাবেশে আমার সমাধানটি শেষ হয়েছিল (আমার ক্ষেত্রে "প্রচলিত" নামে পরিচিত):

   [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event)]
   public class DisplayNameLocalizedAttribute : DisplayNameAttribute
   {
      public DisplayNameLocalizedAttribute(Type resourceManagerProvider, string resourceKey)
         : base(Utils.LookupResource(resourceManagerProvider, resourceKey))
      {
      }
   }

সংস্থানটি দেখার জন্য কোড সহ:

  internal static string LookupResource(Type resourceManagerProvider, string resourceKey)
  {
     foreach (PropertyInfo staticProperty in  resourceManagerProvider.GetProperties(BindingFlags.Static | BindingFlags.NonPublic))
     {
        if (staticProperty.PropertyType == typeof(System.Resources.ResourceManager))
        {
           System.Resources.ResourceManager resourceManager = (System.Resources.ResourceManager)staticProperty.GetValue(null, null);
           return resourceManager.GetString(resourceKey);
        }
     }

     return resourceKey; // Fallback with the key name
  }

সাধারণ ব্যবহার হবে:

class Foo
{
      [Common.DisplayNameLocalized(typeof(Resources.Resource), "CreationDateDisplayName"),
      Common.DescriptionLocalized(typeof(Resources.Resource), "CreationDateDescription")]
      public DateTime CreationDate
      {
         get;
         set;
      }
}

রিসোর্স কী-এর জন্য আক্ষরিক স্ট্রিংগুলি ব্যবহার করার কারণে কি বেশ কুৎসিত। সেখানে একটি ধ্রুবক ব্যবহারের অর্থ রিসোর্সগুলি ডিজাইনার.এসগুলি সংশোধন করা যা সম্ভবত কোনও ভাল ধারণা নয়।

উপসংহার: আমি এতে সন্তুষ্ট নই, তবে মাইক্রোসফ্ট সম্পর্কে আমি খুব কম খুশি যারা এই ধরণের সাধারণ কাজের জন্য দরকারী কিছু সরবরাহ করতে পারে না।


খুব দরকারী. ধন্যবাদ। ভবিষ্যতে, আমি আশা করি মাইক্রোসফ্ট একটি দুর্দান্ত সমাধান নিয়ে আসবে যা উত্সগুলি উল্লেখ করার জন্য দৃ strongly়ভাবে টাইপ করা উপায় সরবরাহ করে।
জনি ওশিকা

হ্যাঁ এই স্ট্রিং স্টাফগুলি সত্যই শক্তভাবে চুষে ফেলেছে :( বৈশিষ্ট্যটি ব্যবহার করে এমন কোনও সম্পত্তির নাম যদি আপনি পেয়ে থাকেন তবে কনফিগারেশনের মাধ্যমে আপনি এটি কনভেনশনে করতে পারতেন, তবে এটি সম্ভব হবে না বলে মনে হয় "দৃ strongly়ভাবে লেখা") আপনি যে
পরিমাপগুলি

এটি একটি ভাল সমাধান। আমি কেবল ResourceManagerসম্পত্তি সংগ্রহের মাধ্যমে পুনরাবৃত্তি করব না । পরিবর্তে আপনি কেবল প্যারামিটারে সরবরাহিত ধরণ থেকে সরাসরি সম্পত্তিটি পেতে পারেন:PropertyInfo property = resourceManagerProvider.GetProperty(resourceKey, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
ম্যাকসিমিলিয়ান মাজার

1
রিসোর্স কীগুলি স্বয়ংক্রিয়ভাবে তৈরি করতে @ zielu1 এর T4 টেমপ্লেটের সাথে এটি একত্রিত করুন এবং আপনার যোগ্য বিজয়ী রয়েছে!
ডেভিড কেভেনি

19

C # 6 তে ডিসপ্লে অ্যাট্রিবিউট ( সিস্টেম.কম্পোনাল মোডেল.ডাটা অ্যানোটেশনগুলি থেকে) এবং নেমোফ () এক্সপ্রেশন ব্যবহার করে আপনি একটি স্থানীয় এবং দৃ strongly়ভাবে টাইপযুক্ত সমাধান পাবেন।

[Display(ResourceType = typeof(MyResources), Name = nameof(MyResources.UserName))]
public string UserName { get; set; }

1
এই উদাহরণে, "মাই রিসোর্সেস" কী? একটি দৃ strongly়ভাবে টাইপ করা রেক্স ফাইল? একটি কাস্টম ক্লাস?
গ্রেগ

14

ধ্রুবক তৈরি করতে আপনি টি 4 ব্যবহার করতে পারেন। আমি একটি লিখেছি:

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Xml.dll" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Xml.XPath" #>
using System;
using System.ComponentModel;


namespace Bear.Client
{
 /// <summary>
 /// Localized display name attribute
 /// </summary>
 public class LocalizedDisplayNameAttribute : DisplayNameAttribute
 {
  readonly string _resourceName;

  /// <summary>
  /// Initializes a new instance of the <see cref="LocalizedDisplayNameAttribute"/> class.
  /// </summary>
  /// <param name="resourceName">Name of the resource.</param>
  public LocalizedDisplayNameAttribute(string resourceName)
   : base()
  {
   _resourceName = resourceName;
  }

  /// <summary>
  /// Gets the display name for a property, event, or public void method that takes no arguments stored in this attribute.
  /// </summary>
  /// <value></value>
  /// <returns>
  /// The display name.
  /// </returns>
  public override String DisplayName
  {
   get
   {
    return Resources.ResourceManager.GetString(this._resourceName);
   }
  }
 }

 partial class Constants
 {
  public partial class Resources
  {
  <# 
   var reader = XmlReader.Create(Host.ResolvePath("resources.resx"));
   var document = new XPathDocument(reader);
   var navigator = document.CreateNavigator();
   var dataNav = navigator.Select("/root/data");
   foreach (XPathNavigator item in dataNav)
   {
    var name = item.GetAttribute("name", String.Empty);
  #>
   public const String <#= name#> = "<#= name#>";
  <# } #>
  }
 }
}

আউটপুট কেমন হবে?
ইরফান্দার

9

এটি একটি পুরানো প্রশ্ন, তবে আমি মনে করি এটি একটি খুব সাধারণ সমস্যা, এবং এখানে এমভিসি 3 তে আমার সমাধান রয়েছে।

প্রথমত, বাজে স্ট্রিংগুলি এড়ানোর জন্য ধ্রুবক তৈরি করতে একটি টি 4 টেম্পলেট প্রয়োজন। আমাদের কাছে একটি সংস্থান ফাইল রয়েছে 'Labels.resx' সমস্ত লেবেল স্ট্রিং ধারণ করে। সুতরাং T4 টেমপ্লেট সরাসরি রিসোর্স ফাইলটি ব্যবহার করে,

<#@ template debug="True" hostspecific="True" language="C#" #>
<#@ output extension=".cs" #>
<#@ Assembly Name="C:\Project\trunk\Resources\bin\Development\Resources.dll" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Collections" #>
<#@ import namespace="System.Globalization" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Resources" #>
<#
  var resourceStrings = new List<string>();
  var manager = Resources.Labels.ResourceManager;

  IDictionaryEnumerator enumerator = manager.GetResourceSet(CultureInfo.CurrentCulture,  true, true)
                                             .GetEnumerator();
  while (enumerator.MoveNext())
  {
        resourceStrings.Add(enumerator.Key.ToString());
  }
#>     

// This file is generated automatically. Do NOT modify any content inside.

namespace Lib.Const{
        public static class LabelNames{
<#
            foreach (String label in resourceStrings){
#>                    
              public const string <#=label#> =     "<#=label#>";                    
<#
           }    
#>
    }
}

তারপরে, 'ডিসপ্লেনাম' স্থানীয়করণের জন্য একটি এক্সটেনশন পদ্ধতি তৈরি হবে,

using System.ComponentModel.DataAnnotations;
using Resources;

namespace Web.Extensions.ValidationAttributes
{
    public static class ValidationAttributeHelper
    {
        public static ValidationContext LocalizeDisplayName(this ValidationContext    context)
        {
            context.DisplayName = Labels.ResourceManager.GetString(context.DisplayName) ?? context.DisplayName;
            return context;
        }
    }
}

'Label.resx' থেকে স্বয়ংক্রিয়ভাবে পড়তে 'ডিসপ্লেনাম' বৈশিষ্ট্যটি 'ডিসপ্লে লেবেল' বৈশিষ্ট্য দ্বারা প্রতিস্থাপন করা হয়েছে,

namespace Web.Extensions.ValidationAttributes
{

    public class DisplayLabelAttribute :System.ComponentModel.DisplayNameAttribute
    {
        private readonly string _propertyLabel;

        public DisplayLabelAttribute(string propertyLabel)
        {
            _propertyLabel = propertyLabel;
        }

        public override string DisplayName
        {
            get
            {
                return _propertyLabel;
            }
        }
    }
}

এই সমস্ত প্রস্তুতি কাজ করার পরে, সেই ডিফল্ট বৈধতা বৈশিষ্ট্যগুলিকে স্পর্শ করার সময়। আমি উদাহরণ হিসাবে 'প্রয়োজনীয়' বৈশিষ্ট্যটি ব্যবহার করছি,

using System.ComponentModel.DataAnnotations;
using Resources;

namespace Web.Extensions.ValidationAttributes
{
    public class RequiredAttribute : System.ComponentModel.DataAnnotations.RequiredAttribute
    {
        public RequiredAttribute()
        {
          ErrorMessageResourceType = typeof (Errors);
          ErrorMessageResourceName = "Required";
        }

        protected override ValidationResult IsValid(object value, ValidationContext  validationContext)
        {
            return base.IsValid(value, validationContext.LocalizeDisplayName());
        }

    }
}

এখন, আমরা আমাদের মডেলগুলিতে এই বৈশিষ্ট্যগুলি প্রয়োগ করতে পারি,

using Web.Extensions.ValidationAttributes;

namespace Web.Areas.Foo.Models
{
    public class Person
    {
        [DisplayLabel(Lib.Const.LabelNames.HowOldAreYou)]
        public int Age { get; set; }

        [Required]
        public string Name { get; set; }
    }
}

ডিফল্টরূপে, সম্পত্তি নামটি 'Label.resx' দেখার জন্য কী হিসাবে ব্যবহৃত হয়, তবে আপনি যদি এটি 'ডিসপ্লে লেবেল'-এর মাধ্যমে সেট করেন তবে এটি পরিবর্তে এটি ব্যবহার করবে।


6

আপনি i18n সরবরাহ করতে ডিসপ্লে নেমঅ্যাট্রিবিউটকে একটি পদ্ধতিতে ওভাররাইড করে সাবক্লাস করতে পারেন। তাই ভালো. সম্পাদনা করুন: কীটির জন্য ধ্রুবক ব্যবহারের জন্য আপনাকে বসতি স্থাপন করতে হতে পারে।

using System;
using System.ComponentModel;
using System.Windows.Forms;

class Foo {
    [MyDisplayName("bar")] // perhaps use a constant: SomeType.SomeResName
    public string Bar {get; set; }
}

public class MyDisplayNameAttribute : DisplayNameAttribute {
    public MyDisplayNameAttribute(string key) : base(Lookup(key)) {}

    static string Lookup(string key) {
        try {
            // get from your resx or whatever
            return "le bar";
        } catch {
            return key; // fallback
        }
    }
}

class Program {
    [STAThread]
    static void Main() {
        Application.Run(new Form { Controls = {
            new PropertyGrid { SelectedObject =
                new Foo { Bar = "abc" } } } });
    }
}

2

আমি আমার ক্ষেত্রে সমাধান করুন এইভাবে ব্যবহার

[LocalizedDisplayName("Age", NameResourceType = typeof(RegistrationResources))]
 public bool Age { get; set; }

কোড সহ

public sealed class LocalizedDisplayNameAttribute : DisplayNameAttribute
{
    private PropertyInfo _nameProperty;
    private Type _resourceType;


    public LocalizedDisplayNameAttribute(string displayNameKey)
        : base(displayNameKey)
    {

    }

    public Type NameResourceType
    {
        get
        {
            return _resourceType;
        }
        set
        {
            _resourceType = value;
            _nameProperty = _resourceType.GetProperty(base.DisplayName, BindingFlags.Static | BindingFlags.Public);
        }
    }

    public override string DisplayName
    {
        get
        {
            if (_nameProperty == null)
            {
                return base.DisplayName;
            }

            return (string)_nameProperty.GetValue(_nameProperty.DeclaringType, null);
        }
    }

}

1

ঠিক আছে, সমাবেশ হয় Microsoft.VisualStudio.Modeling.Sdk.dll। যা ভিজ্যুয়াল স্টুডিও এসডিকে (ভিজ্যুয়াল স্টুডিও ইন্টিগ্রেশন প্যাকেজ সহ) নিয়ে আসে।

তবে এটি আপনার বৈশিষ্ট্যের মতো একইভাবে ব্যবহৃত হবে; অবিচ্ছিন্নভাবে শক্তিশালী ধরণের সংস্থানগুলি ব্যবহার করার কোনও উপায় নেই কারণ তারা ধ্রুবক নয়।


0

আমি VB.NET কোডের জন্য ক্ষমা চাইছি, আমার সি # কিছুটা মরিচা ... তবে আপনি ধারণাটি পাবেন, তাই না?

প্রথমত, একটি নতুন শ্রেণি তৈরি করুন LocalizedPropertyDescriptor:, যা উত্তরাধিকার সূত্রে প্রাপ্ত PropertyDescriptorDisplayNameএই জাতীয় সম্পত্তিটিকে ওভাররাইড করুন :

Public Overrides ReadOnly Property DisplayName() As String
         Get
            Dim BaseValue As String = MyBase.DisplayName
            Dim Translated As String = Some.ResourceManager.GetString(BaseValue)
            If String.IsNullOrEmpty(Translated) Then
               Return MyBase.DisplayName
            Else
               Return Translated
           End If
    End Get
End Property

Some.ResourceManager আপনার অনুবাদগুলি রয়েছে এমন রিসোর্স ফাইলের রিসোর্স ম্যানেজার।

এর পরে, ICustomTypeDescriptorস্থানীয় বৈশিষ্ট্য সহ শ্রেণিতে প্রয়োগ করুন এবং GetPropertiesপদ্ধতিটি ওভাররাইড করুন :

Public Function GetProperties() As PropertyDescriptorCollection Implements System.ComponentModel.ICustomTypeDescriptor.GetProperties
    Dim baseProps As PropertyDescriptorCollection = TypeDescriptor.GetProperties(Me, True)
    Dim LocalizedProps As PropertyDescriptorCollection = New PropertyDescriptorCollection(Nothing)

    Dim oProp As PropertyDescriptor
    For Each oProp In baseProps
        LocalizedProps.Add(New LocalizedPropertyDescriptor(oProp))
    Next
    Return LocalizedProps
End Function

আপনি এখন কোনও উত্স ফাইলে একটি মানের রেফারেন্স সঞ্চয় করতে 'প্রদর্শন নাম` বৈশিষ্ট্যটি ব্যবহার করতে পারেন ...

<DisplayName("prop_description")> _
Public Property Description() As String

prop_description রিসোর্স ফাইলে মূল কী।


আপনার সমাধানের প্রথম অংশটি হ'ল আমি কী করেছি ... যতক্ষণ না "সমাধান কিছু আছে? রিসোর্স ম্যানেজার?" প্রশ্ন। আমার কি দ্বিতীয় আক্ষরিক স্ট্রিং যেমন "মাইআস্যাব্পশন.সেসোসেস.সেসোসোর্ট" দেওয়ার কথা রয়েছে? উপায় খুব বিপজ্জনক! দ্বিতীয় অংশ হিসাবে (আইসুস্টমটাইপডেস্কেক্টর) আমি এটি আসলে কার্যকর মনে করি না
পাওয়ারকিকি

অনুবাদকৃত ডিসপ্লেনাম ব্যতীত আপনার আর কিছুর প্রয়োজন না থাকলে মার্ক গ্র্যাভেলের সমাধানটি হ'ল উপায় - আমি অন্যান্য সামগ্রীর জন্যও কাস্টম বর্ণনাকারী ব্যবহার করি এবং এটিই আমার সমাধান। যদিও কোনও ধরণের কী সরবরাহ না করে এটি করার কোনও উপায় নেই।
ভিনসেন্ট ভ্যান ডেন বার্গে
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.