I am using a personal solution on http://www.superior-extensions.com/, but I had to change many thing :
First part : update you code.
In Nop.BusinessLogic.SEO.SEOHelper I have added this :
private static Language defaultLanguage = null;
public static Language DefaultLanguage
{
get
{
if (defaultLanguage == null)
{
defaultLanguage = NopSolutions.NopCommerce.BusinessLogic.Directory.LanguageManager.GetAllLanguages(false)[0];
}
return defaultLanguage;
}
}
private static string ExtractLocaleSubFolderFromCulture(string culture)
{
bool locFolderUseLanguage = true; //You can set to false to use country par of the culture
if (locFolderUseLanguage)
{
//in fr-CA, uses fr
return culture.Substring(0, culture.IndexOf('-'));
}
else
{
//in fr-CA, uses CA
return culture.Substring(culture.IndexOf('-') + 1);
}
}
public static string GetLocaleSubFolder()
{
if (NopContext.Current != null && NopContext.Current.WorkingLanguage != null)
{
if (NopContext.Current.WorkingLanguage.LanguageId != DefaultLanguage.LanguageId)
{
string locFolder = ExtractLocaleSubFolderFromCulture(NopContext.Current.WorkingLanguage.LanguageCulture);
return locFolder + "/";
}
else
return "";
}
else
return "";
}
This code helps you to build the needed part (/us/, /de/, /fr/ etc...) that you want to add to your urls. This new part is called localeSubFolder, and is empty for the default language of you site.
Remark : it is using a static field to improve perfs, so need an application restart if you change default language...
In each proc that gives you seo url, I added a call to GetLocaleSubFolder. Exemple with GetProductUrl :
public static string GetProductUrl(Product product)
{
if (product == null)
throw new ArgumentNullException("product");
string seName = GetSEName(product.SEName);
if (String.IsNullOrEmpty(seName))
{
seName = GetSEName(product.Name);
}
string url = string.Format(SettingManager.GetSettingValue("SEO.Product.UrlRewriteFormat"),
CommonHelper.GetStoreLocation() + /* added here */ GetLocaleSubFolder(), product.ProductId, seName);
return url.ToLowerInvariant();
}
You should do the same for each proc GetXXXUrl
Second part : update url rewrite
You should also update the file UrlRewriting.config to add new rules, because new urls contains another / and regex will not recognize you url.
for example add a new rule for localized products, juste to add /[\w-]*/ in the regex :
<add name="LocProductDetailsRewrite" virtualUrl="^~/[\w-]*/products/([0-9]*)-([\w-]*)\.aspx(?:\?(.*))?"
rewriteUrlParameter="ExcludeFromClientQueryString"
destinationUrl="~/product.aspx?productid=$1&$3"
ignoreCase="true" />
Last part : tell search engines what are your goals.
Google allows you to tell explicitly which country is concerned by which subfolder. Go to the webmaster tools, validate you site and each subfolder, then Site configuration -> Settings -> Geographic target
This solution is not perfect :
If you manually change url in the browser to add /en/ it does not change the current language.
We could change the static field into a cached value, use settings to store locFolderUseLanguage value...