Localized image names

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.
8 лет назад
Hi, I've just discovered that I can't have product image names in another language (in this case Japanese), I've been looking at the code namely pictureservice.cs and I'm wondering if anyone can suggest a way that I might implement it, the site will have both english and japanese language installed and the images will be stored in the database. I'm using some plugins too so they use the same service (I did think about implementing it on the front end where the SEO tags are crunched, but that would mean any product image served by the plugins would still be in the original format, so it seems modifying the picture service would be the logical route), any ideas how I might get a handle on the product model from pictureservice.cs? Also, would I need to modify thumb creation as well? I'm guessing I might need thumbs for each language version?

Thanks in advance
8 лет назад
Hi sedgey,

Product images do support multi language names and titles. I am not sure if this is what you mean, but for example lets take the PrepareProductDetailsPageModel method which prepares the product details page model with the main picture and picture thumbs.

If you see in the beginning of the method where the standard properties are prepared you will see:

Name = product.GetLocalized(x => x.Name),


which will take the localized product name. Then when preparing the images you will see:

Title = string.Format(_localizationService.GetResource("Media.Product.ImageLinkTitleFormat.Details"), model.Name),
AlternateText = string.Format(_localizationService.GetResource("Media.Product.ImageAlternateTextFormat.Details"), model.Name),


So when preparing the product pictures title and alt, the localized name will be used. Then in the razor view these are used to render the img tag.

<img alt="@Model.DefaultPictureModel.AlternateText" src="@Model.DefaultPictureModel.ImageUrl" title="@Model.DefaultPictureModel.Title" itemprop="image" id="[email protected]" />


So if I understand your question correctly, the title and alt text for each product image will be localized.

Is this what you are searching for?

If you mean that you need to have different product images per language, then this is not supported and although it seems strange to have different images per language, you will need to implement this by yourself which will be quite a lot of work and I personally would not recommend it.

Hope that helps.

Best Regards,
Stefan.
8 лет назад
Hi Stefan,
Thanks for your response, I found that code in the product controller and was intending to use it in picture service. To better explain what I mean right now the image tag src fields use the seo friendly name of the product in english (on the product pages)

e.g.  0000088_sample-logo-t-shirt_95.jpg

this is good for English sites, but I'm looking to have the other language in the name for image SEO purposes

e.g. 0000088_サンプルロゴTシャツ_95.jpg

I'm guessing there will need to be different thumbs for each language for this work? And probably I can use the title text you mentioned in the code snippet to do it, but I'm just wondering what's the best approach.

best David
8 лет назад
Hi David,

I now see what you are looking for. This will be quite tricky to implement in a reasonable way. What you can try is have a look at the PictureService.GetPictureUrl method implementation. There you can add your logic for generating images for every language. For example, when a picture URL is requested, you will need to iterate through your languages and generate a thumb picture for every language where the SeoFilename should be the localized name of the product. Then in the public part when using the Picture Url for the src tag, you will need to replace the SeoFilename with the model.name in the file name of the image.

With this approach you should have the following in mind:

1. When creating thumbs for each language, you will need to write a query that will return a product by a picture id (if there is such) and then get the localized name of that product for the language you are generating a picture. This should be done with some LINQ query so that you will not iterate through all products and search for the picture id.

Thus when a picture Url is requested you will create two files instead of one, for example:

0000088_sample-logo-t-shirt_95.jpg
0000088_サンプルロゴTシャツ_95.jpg

2. The PictureService.GetPictureUrl method is used for all kind of images and not only for product images. So you should have this in mind when writing your custom logic. You will not necessary have a product with the requested picture id every time.

3. In the public part when creating the new file name for your language have in mind the way the file name is being generated case when there is a resizing of the images and when there isn't.

As I said, this is not a perfect solution, but can give you some hints on how to implement it if you really need this functionality.

Hope this will help.

Best Regards,
Stefan
8 лет назад
Thanks. In the meantime I've created a work item for this task
8 лет назад
Hi Andrei,

That is great. This can be done not only for products but also for all types of entities like categories, manufacturers, vendors, etc. I am sure a lot of customers will take advantage of this new feature.

Best Regards,
Stefan
8 лет назад
Hi Stefan,
Thanks for that, so far I've got a method to return a localized product name from a picture ID, I had to inject an IWorkContext, IRepository<Product> and a ILocalizationService to get it to work. (Note: you need to change the signature in AzurePictureService to get it to build as it inherits from PictureService.cs).
    /// <summary>
    /// Gets the product name localized by picture identifier.
    /// </summary>
    /// <param name="pictureId">The picture identifier.</param>
    /// <returns></returns>
    public virtual string GetProductNameLocalizedByPictureId(int pictureId)
    {
      if (_workContext.WorkingLanguage.Name == "English") return string.Empty;
      if (_productRepository == null) return string.Empty;
      var product = (from p in _productRepository.Table
        join pp in _productPictureRepository.Table on p.Id equals pp.PictureId
        orderby pp.DisplayOrder
        where pp.PictureId == pictureId
        select p).FirstOrDefault();
      return product != null ? string.Format(_localizationService.GetResource("Media.Product.ImageLinkTitleFormat.Details"), product.Name) : string.Empty;
    }
8 лет назад
Hi David,

That is a good start. Now you should make use of this method. You can have something like the code below where you will place the generating of the pictures in a foreach statement that will loop through the languages for your store.

string seoFileName = picture.SeoFilename; // = GetPictureSeName(picture.SeoFilename); //just for sure
foreach(var language in languageService.GetAllLanguages())
{
    var seoLocalizedFileName = GetProductNameLocalizedByPictureId(picture.Id);
    if(!String.IsNullOrEmpty(seoLocalizedFileName))
    {
        seoFileName = seoLocalizedFileName;
    }


    lock (s_lock)
    {
        //seoFileName = picture.SeoFilename; // = GetPictureSeName(picture.SeoFilename); //just for sure
        if (targetSize == 0)
     .......
     .......
}


where you will need to resolve the language service so that you will need to access the different languages.

If all works as expected, when you open a product page you should see in the content/images/thumbs folder two images with different file names generated for every picture.

Hope this helps.

Best Regards,
Stefan
8 лет назад
Ok, this was what worked in the end, I decided the category and picture images were what we actually wanted to have with localized names. The reverse product name and category name looks ups are not great performance wise but lookups could be decreased by having the picture entity have an image type. Hope this helps others looking for the same functionality until the work item is completed, thanks Stefan for all your help! Best D


    #region GetPictureUrl
    /// <summary>
    /// Get a picture URL
    /// </summary>
    /// <param name="picture">Picture instance</param>
    /// <param name="targetSize">The target picture size (longest side)</param>
    /// <param name="showDefaultPicture">A value indicating whether the default picture is shown</param>
    /// <param name="storeLocation">Store location URL; null to use determine the current store location automatically</param>
    /// <param name="defaultPictureType">Default picture type</param>
    /// <returns>Picture URL</returns>
    public virtual string GetPictureUrl(Picture picture, int targetSize = 0, bool showDefaultPicture = true, string storeLocation = null, PictureType defaultPictureType = PictureType.Entity)
    {
      string url = string.Empty;
      byte[] pictureBinary = null;
      if (picture != null)
        pictureBinary = LoadPictureBinary(picture);
      if (picture == null || pictureBinary == null || pictureBinary.Length == 0)
      {
        if (showDefaultPicture)
        {
          url = GetDefaultPictureUrl(targetSize, defaultPictureType, storeLocation);
        }
        return url;
      }

      var lastPart = GetFileExtensionFromMimeType(picture.MimeType);
      var currentThumbFileName = string.Empty;
      if (picture.IsNew)
      {
        DeletePictureThumbs(picture);

        //we do not validate picture binary here to ensure that no exception ("Parameter is not valid") will be thrown
        picture = UpdatePicture(picture.Id,
          pictureBinary,
          picture.MimeType,
          picture.SeoFilename,
          picture.AltAttribute,
          picture.TitleAttribute,
          false,
          false);
      }

      foreach (var language in _languageService.GetAllLanguages())
      {
        string seoFileName;
        var localizedName = GetLocalizedNameByPicture(picture, language);
        if (!string.IsNullOrEmpty(localizedName))
        {
          seoFileName = localizedName;
        }
        else
        {
          seoFileName = GetPictureSeName(picture.SeoFilename);
        }

        lock (s_lock)
        {
          string thumbFileName;
          if (targetSize == 0)
          {
            //original size (no resizing required)
            thumbFileName = !string.IsNullOrEmpty(seoFileName)
              ? string.Format("{0}_{1}.{2}", picture.Id.ToString("0000000"), seoFileName, lastPart)
              : string.Format("{0}.{1}", picture.Id.ToString("0000000"), lastPart);
            var thumbFilePath = GetThumbLocalPath(thumbFileName);
            if (!GeneratedThumbExists(thumbFilePath, thumbFileName))
            {
              SaveThumb(thumbFilePath, thumbFileName, pictureBinary);
            }
          }
          else
          {
            //resizing required
            thumbFileName = !string.IsNullOrEmpty(seoFileName)
              ? string.Format("{0}_{1}_{2}.{3}", picture.Id.ToString("0000000"), seoFileName, targetSize, lastPart)
              : string.Format("{0}_{1}.{2}", picture.Id.ToString("0000000"), targetSize, lastPart);
            var thumbFilePath = GetThumbLocalPath(thumbFileName);
            if (!GeneratedThumbExists(thumbFilePath, thumbFileName))
            {
              using (var stream = new MemoryStream(pictureBinary))
              {
                Bitmap b = null;
                try
                {
                  //try-catch to ensure that picture binary is really OK. Otherwise, we can get "Parameter is not valid" exception if binary is corrupted for some reasons
                  b = new Bitmap(stream);
                }
                catch (ArgumentException exc)
                {
                  _logger.Error(string.Format("Error generating picture thumb. ID={0}", picture.Id), exc);
                }
                if (b == null)
                {
                  //bitmap could not be loaded for some reasons
                  return url;
                }

                using (var destStream = new MemoryStream())
                {
                  var newSize = CalculateDimensions(b.Size, targetSize);
                  ImageBuilder.Current.Build(b, destStream, new ResizeSettings
                  {
                    Width = newSize.Width,
                    Height = newSize.Height,
                    Scale = ScaleMode.Both,
                    Quality = _mediaSettings.DefaultImageQuality
                  });
                  var destBinary = destStream.ToArray();
                  SaveThumb(thumbFilePath, thumbFileName, destBinary);
                  b.Dispose();
                }
              }
            }
          }

          if (_workContext.WorkingLanguage == language)
          {
            currentThumbFileName = thumbFileName;
          }
        }
      }
      url = GetThumbUrl(currentThumbFileName, storeLocation);
      return url;
    }
    #endregion

    #region GetLocalizedNameByPicture
    /// <summary>
    /// Gets the localized name by picture.
    /// </summary>
    /// <param name="picture">The picture.</param>
    /// <param name="language">The language.</param>
    /// <returns></returns>
    public virtual string GetLocalizedNameByPicture(Picture picture, Language language)
    {
      if (language.Name == "English") return GetPictureSeName(picture.SeoFilename);
      var category = (from c in _categoryRepository.Table
        where c.PictureId == picture.Id
        select c).FirstOrDefault();
      if (category != null)
        return SeoExtensions.GetSeName(category.GetLocalized(x => x.Name, language.Id), true, true);
      var product = (from p in _productRepository.Table
               join pp in _productPictureRepository.Table on p.Id equals pp.ProductId
               orderby pp.DisplayOrder
               where pp.PictureId == picture.Id
               select p).FirstOrDefault();
      if (product != null)
        return SeoExtensions.GetSeName(product.GetLocalized(x => x.Name, language.Id), true, true);
      var seoFileName = GetPictureSeName(picture.SeoFilename);
      return seoFileName ?? string.Empty;
    }
    #endregion
7 лет назад
I saw the work item described in this link https://github.com/nopSolutions/nopCommerce/issues/1216 . Will this feature in 3.90 be able to support multi language support for AltAttribute ?
This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.