X
dnn.blog.
Back

Rapid Module Development Part 2 - The multilanguage thing…

Since I get to know DNN back in 2008, one subject grinds my gears all the time and this is the multilanguage feature of modules. Back In 2008/2009 there were a lot of rumors and discussions about ML. The corp started with building their multilanguage solution and I remember some intense discussion with Sebastian and others about this topic. At the end the corp solution does not help me building my ML modules and so I had to find my own way to handle this.

In this blog I’ll show you how you can implement ML features without any hassle. My method is very straight forward and you could use this as a pattern for all your modules. There is no more increased effort needed to make your module ML compliant if you follow these guidelines.

The sample module

We want to build a product module in this tutorial. To keep it simple, we bind the product to the module: Every module shows only his module-specific product. In the action menu, we’ll have the option to edit the product. All text properties of the product should be ML capable.

Database Design

It is good practise to prefix your table names with something. My table names  start with the module name as prefix followed by an underline and the real table name. In this sample we have a product table with some fields that are language independent and a compagnion table with the same name and the Suffix **Lang **that contain all the language dependent fields. As the third part in this game we define an SQL View containing all the fields joined from both tables for display purposes.

Database Design

After the design phase  I let my favourite tool XCase  generate the needed SQL code to create everything in SQL server:

Generated SQL

IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = object_id(N'{databaseOwner}[{objectQualifier}BBLanguagePattern_Product]') and OBJECTPROPERTY(id, N'IsTable') = 1)
   BEGIN
      CREATE TABLE {databaseOwner}[{objectQualifier}BBLanguagePattern_Product] ( 
         ProductId INT NOT NULL IDENTITY (1,1),
         ModuleId INT NULL,
         Image NVARCHAR(120) NULL,
         Price DECIMAL(12,4) NULL,
         Tax CHAR(10) NULL
      )
      ALTER TABLE {databaseOwner}[{objectQualifier}BBLanguagePattern_Product] ADD CONSTRAINT PK_BBLanguagePattern_Product PRIMARY KEY NONCLUSTERED  (ProductId ASC) WITH ( IGNORE_DUP_KEY = OFF)
   END
GO
IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = object_id(N'{databaseOwner}[{objectQualifier}BBLanguagePattern_ProductLang]') and OBJECTPROPERTY(id, N'IsTable') = 1)
   BEGIN
      CREATE TABLE {databaseOwner}[{objectQualifier}BBLanguagePattern_ProductLang] ( 
         ProductId INT NULL,
         Language CHAR(5) NULL,
         Name NVARCHAR(40) NULL,
         Shortdescription NVARCHAR(400) NULL,
         Longdescription NVARCHAR(MAX) NULL
      )
   END
GO
IF NOT EXISTS (SELECT 1 FROM sys.objects where name='FK_ProductLang' and type='F')
   ALTER TABLE {databaseOwner}[{objectQualifier}BBLanguagePattern_ProductLang] WITH NOCHECK ADD CONSTRAINT FK_ProductLang FOREIGN KEY ( ProductId ) REFERENCES {databaseOwner}[{objectQualifier}BBLanguagePattern_Product] ( ProductId ) ON DELETE CASCADE
GO
CREATE VIEW BBLanguagePattern_ProductLoc AS
SELECT  ALL    BBLanguagePattern_Product.ProductId , BBLanguagePattern_Product.ModuleId ,  
      BBLanguagePattern_Product.Image , BBLanguagePattern_Product.Tax ,  
      BBLanguagePattern_Product.Price , ProductLang.Language , ProductLang.Name ,  
      ProductLang.Shortdescription , ProductLang.Longdescription 
   FROM BBLanguagePattern_Product INNER JOIN BBLanguagePattern_ProductLang ProductLang ON 
     BBLanguagePattern_Product.ProductId = ProductLang.ProductId
GO

I paste this code into the host/SQL window of DNN and let it create the tables and the view.

Setting up the project

Within Visual studio we create a new project depending on my Bitboxx DNN 7 project template. Project name is equal to the prefix of my database tables – BBLanguagePattern in this case. If the prefixes differ from the project name, you have to edit the file Models/Generated/Database.tt and alter the value in prefix.

// Settings
ConnectionStringName = "SiteSqlServer";            // Uses last connection string in config if not specified
Namespace = "Bitboxx.DNNModules.BBLanguagePattern";
RepoName = ".";
GenerateOperations = false;
GeneratePocos = true;
GenerateCommon = false;
ClassPrefix = "";
ClassSuffix = "";
TrackModifiedColumns = false;
IncludeViews = true;
// Read schema
Tables tables = LoadTables();
string prefix = "BBLanguagePattern";

If everything is OK, saving the Database.tt file now generates Database.cs containing all the needed POCO’s for our tables and for the view:

using System;
using System.Web.Caching;
using Bitboxx.DNNModules.Controls;
using DotNetNuke.ComponentModel.DataAnnotations;
namespace Bitboxx.DNNModules.BBLanguagePattern
{
    [TableName("BBLanguagePattern_Product")]
    [PrimaryKey("ProductId")]
	[Cacheable("BBLanguagePattern_Product", CacheItemPriority.Normal, 20)]
    public partial class ProductInfo     
	{
        public int ProductId { get; set; }
        public string Image { get; set; }
        public decimal? Price { get; set; }
        public string Tax { get; set; }
        public int? ModuleId { get; set; }
    }
	[Serializable]
    [TableName("BBLanguagePattern_ProductLang")]
	[Cacheable("BBLanguagePattern_ProductLang", CacheItemPriority.Normal, 20)]
    public partial class ProductLangInfo : ILanguageEditorInfo     
	{
        public int? ProductId { get; set; }
        public string Language { get; set; }
		[LanguageEditor("TextBox" , MaxLength = 40)]
        public string Name { get; set; }
		[LanguageEditorAttribute("TextBox" , MaxLength = 400)]
        public string Shortdescription { get; set; }
		[LanguageEditorAttribute("TextEditor", Height = "600px")]
        public string Longdescription { get; set; }
    }
    [TableName("BBLanguagePattern_ProductLoc")]
	[Cacheable("BBLanguagePattern_ProductLoc", CacheItemPriority.Normal, 20)]
    public partial class ProductLocInfo     
	{
        public int ProductId { get; set; }
        public int? ModuleId { get; set; }
        public string Image { get; set; }
        public string Tax { get; set; }
        public decimal? Price { get; set; }
        public string Language { get; set; }
        public string Name { get; set; }
        public string Shortdescription { get; set; }
        public string Longdescription { get; set; }
    }
}

Please take a look at the POCO of the ~Lang table. This one is inherited from the ILanguageEditorInfo, an interface specifically for the LanguageEditor to work later. I tweaked the PetaPoco tt files to generate this automatically if we have a table and its Lang counterpart. Also the string properties in the ~Lang POCO now have attributes defining which type of editor control is used to edit (TextBox,TextEditor) and some other properties (MaxLength,Width, Height,Rows,Label).

The LanguageEditor Control

Visual Studio does not know the attributes and the interface at the moment, so now we have to add the Language Editor Control to the project. The easiest way to do this is to download it from here and extract all files into a new project folder named Controls. Then open this folder in Windows Explorer, mark all files insides and do a drag&drop action into the project:

Language Editor Control

The View.ascx

In this part of the Rapid Module Development Blog we will only have a fixed display of our product. The templating will be part of another blog post introducing the template control.

Thats how it should look alike:

View.ascx

The ascx-code is simple:

<div id="bblanguagepattern-view">
    <asp:Image runat="server" ID="imgProduct"/>
    <h3><asp:Label runat="server" ID="lblName"/></h3>
    <p><strong><asp:Label runat="server" ID="lblShortDescription"/></strong></p>
    <p><asp:Label runat="server" ID="lblLongDescription"/></p>
    <div style="float:right;text-align:right;background-color:;padding:40px 20px;;">
        <span class="price"><asp:Label runat="server" ID="lblPrice"/></span><br/> 
        <asp:Label runat="server" ID="lblTax"/>
    </div>
</div>

Lets have a look at the code behind:

public partial class View : PortalModuleBase, IActionable
{
    #region Event Handlers
    /// <summary>
    /// Runs when the control is loaded
    /// </summary>
    private void Page_Load(object sender, EventArgs e)
    {
        try
        {
            if (!Page.IsPostBack)
            {
                BBLanguagePatternController controller = new BBLanguagePatternController();
                ProductLocInfo product = controller.GetProductLoc(ModuleId, Thread.CurrentThread.CurrentCulture.Name);
                lblName.Text = product.Name;
                lblShortDescription.Text = product.Shortdescription;
                lblLongDescription.Text = product.Longdescription;
                lblPrice.Text = String.Format("{0:c}",product.Price);
                lblTax.Text = String.Format(LocalizeString("lblTax.Text"), product.Tax);
                imgProduct.ImageUrl = PortalSettings.HomeDirectory + product.Image;
            }
        }
        catch (Exception exc) 
        {
            Exceptions.ProcessModuleLoadException(this, exc);
        }
    }
    #endregion
    #region Interfaces
    public ModuleActionCollection ModuleActions
    {
        get
        {
            ModuleActionCollection Actions = new ModuleActionCollection();
            Actions.Add(GetNextActionID(), Localization.GetString("EditProduct.Action", LocalResourceFile), ModuleActionType.EditContent, "", "edit.gif", EditUrl(), false, SecurityAccessLevel.Edit, true, false);
            return Actions;
        }
    }
    #endregion
}

All we have to do here is instantiating the Controller (BBLanguagePatternController class was built by the project template), retrieve the corresponding ProductLocInfo object (this is the view data for the current UI language and the current moduleId, created with the help of the CRUD-Generator (see first blog part), or coded manually)

public ProductLocInfo GetProductLoc(int moduleId, string language)
{
    using (IDataContext context = DataContext.Instance())
    {
        var repository = context.GetRepository<ProductLocInfo>();
        return repository.Find("WHERE ModuleId = @0 AND Language = @1", moduleId, language).FirstOrDefault();
     }
}

Binding the labels to the ProductLocInfo properties is standard work and we are ready to go!

The Edit.ascx

This is the exciting part. First lets have a look at the UI:

Edit.ascx

So how is this done ? The ascx-code is fairly simple too. We need to register the Language editor control (and the dnn filepicker control for the image) and use them later in the code. To combine the Control with the data to edit, we reference our ProductLangInfo class (containing the LanguageEditor attributes) in the LanguageEditor tag (InternalType=”…”) :

<%@ Control language="C#" Inherits="Bitboxx.DNNModules.BBLanguagePattern.Edit" AutoEventWireup="true" Codebehind="Edit.ascx.cs" %>
<%@ Register TagPrefix="dnn" TagName="Label" Src="~/controls/LabelControl.ascx" %>
<%@ Register TagPrefix="bb" TagName="LanguageEditor" Src="Controls/LanguageEditorControl.ascx" %>
<%@ Register TagPrefix="dnn" TagName="FilePickerUploader" Src="~/controls/filepickeruploader.ascx" %>
<div class="dnnForm bblanguagepattern-productedit dnnClear" id="bblanguagepattern-edit">
    <fieldset>
        <asp:HiddenField ID="hidProductId" runat="server" />
        <div class="dnnFormItem">
            <dnn:Label ID="lblImage" runat="server" ControlName="ctlImage"  Suffix=":"/>
            <dnn:FilePickerUploader ID="ctlImage" runat="server" Required="True" />
        </div>
        <div class="dnnFormItem">
            <dnn:Label ID="lblPrice" runat="server" ControlName="txtPrice"  Suffix=":"/>
            <asp:TextBox ID="txtPrice" runat="server" MaxLength="12" />
        </div>
        <div class="dnnFormItem">
            <dnn:Label ID="lblTax" runat="server" ControlName="txtTax"  Suffix=":"/>
            <asp:TextBox ID="txtTax" runat="server" MaxLength="10" />
        </div>
        <bb:LanguageEditor ID="lngProduct" runat="server" InternalType="Bitboxx.DNNModules.BBLanguagePattern.ProductLangInfo" />
    </fieldset>
    <ul class="dnnActions dnnClear">
        <li><asp:LinkButton CssClass="dnnPrimaryAction" ID="cmdUpdate" runat="server" resourcekey="cmdUpdate" OnClick="cmdUpdate_Click" /></li>
        <li><asp:LinkButton CssClass="dnnSecondaryAction" ID="cmdCancel" runat="server" resourcekey="cmdCancel" OnClick="cmdCancel_Click" CausesValidation="false"/></li>
    </ul>
</div>

At Load event we retrieve the corresponding product and set the values for the textboxes, the filepicker and our LanguageEditor. (The controller method to read the ProductLangs and all other DAL2 methods could also be generated with CRUD-Generator or coded manually)

_product = Controller.GetProductLoc(ModuleId, CurrentLanguage);
if (_product != null)
{
    hidProductId.Value = _product.ProductId.ToString();
    if (!IsPostBack)
    {
        ctlImage.FilePath = _product.Image;
        txtPrice.Text = _product.Price.ToString();
        txtTax.Text = _product.Tax;
        var dbLangs = new List<ILanguageEditorInfo>();
        lngProduct.Langs.Clear();
        foreach (ProductLangInfo productLang in Controller.GetProductLangs(_product.ProductId))
        {
            dbLangs.Add(productLang);
        }
        lngProduct.Langs = dbLangs;
    }
}

In cmdUpdate_Click we read out all values and save them back to the database:

int productId = Convert.ToInt32(hidProductId.Value);
ProductInfo product = new ProductInfo();
product.ProductId = productId;
product.Image = ctlImage.FilePath.Replace("//", "/");
product.Price = Convert.ToDecimal(txtPrice.Text.TrimEnd());
product.Tax = txtTax.Text.TrimEnd();
product.ModuleId = ModuleId;
if (productId > -1)
    Controller.UpdateProduct(product);
else
    productId = Controller.InsertProduct(product);
// Now lets update Language information
lngProduct.UpdateLangs();
Controller.DeleteProductLangs(productId);
foreach (ProductLangInfo lang in lngProduct.Langs)
{
    lang.ProductId = productId;
    Controller.InsertProductLang(lang);
}
Response.Redirect(Globals.NavigateURL(TabId),true);

Summary

With the LanguageEditor control, the help of the CRUD-Generator and the tuned PetaPoco tt files there are no more excuses to write only single language modules. Feel free to download the sample module, test it and ask questions if something is not working.

Download Sample project

Download CRUD Generator

Back
Total: 41 Comment(s)
Michael T. Harwell
Get the dissertation writing service students look for these days with the prime focus being creating a well researched and lively content on any topic. https://paymetodoyourhomework.xyz/">Pay To Write My Test For Me
Monday, June 11, 2018 · reply ·
Michael T. Harwell
Great Information,it has lot for stuff which is informative.I will share the post with my friends. https://paymetodoyourhomework.xyz
Monday, June 11, 2018 · reply ·
Jack
Get information on your site - please continue to write more blog posts on this subject http://www.arbuk.co.uk">Tree Felling Oxford
Wednesday, July 4, 2018 · reply ·
aftabak
You put a bet if the joined last scores of the two groups is finished or unde. http://www.betfaircasino.eu/
Saturday, July 7, 2018 · reply ·
1xbet
The website is looking bit flashy and it catches the visitors eyes. Design is pretty simple and a good user friendly interface.
Saturday, July 7, 2018 · reply ·
aftabak
Genuine gambling clubs must need to constrain their movement since they claim a place and the space is restricted. http://lp.nightrush.com/SE/sport100/?btag=a_8784b_625c_
Sunday, July 8, 2018 · reply ·
ryukin goldfish
I really love this post I will visit again to read your post in a very short time and I hope you will make more posts like this. 
Thursday, July 19, 2018 · reply ·
lfwh
https://goldfisho.com/ I really love this post I will visit again to read your post in a very short time and I hope you will make more posts like this. 
Thursday, July 19, 2018 · reply ·
html color codes
I really love this post I will visit again to read your post in a very short time and I hope you will make more posts like this. https://htmlcolor-codes.com/
Monday, July 23, 2018 · reply ·
red nose pocket pitbull
Thanks for a very interesting blog. What else may I get that kind of info written in such a perfect approach? https://www.pocketpitbull.com/">red nose pocket pitbull
Wednesday, August 1, 2018 · reply ·
Togel Online
Incredible posting this is from you. I am really and truly thrilled to read this marvelous post. You've really impressed me today. I hope you'll continue to do so! http://www.mlaja.org
Thursday, August 2, 2018 · reply ·
ChrisGreen
This is really something new. I am sure that all of the above coding technique is new for people and they can easily tackle different coding issues using your solution. https://www.theacademicpapers.co.uk/assignment-writing-services-uk.php
Saturday, August 4, 2018 · reply ·
Sydney video production
This is only the data I am discovering all over the place. A debt of gratitude is in order for your website, I simply subscribe your online journal. This is a decent blog.. https://copestreetcrew.com.au
Saturday, August 4, 2018 · reply ·
Dissertation Help Uk
Great Info! I Recently Came Across Your Blog And Have Been Reading Along. I Thought I Would Leave My First Comment. I Don’t Know What To Say Except That I Have
Thursday, August 16, 2018 · reply ·
PHP programming Help
Thanx For Sharing Such Useful Post Keep It Up :) https://phphelponline.com
Thursday, August 16, 2018 · reply ·
qdohi
I hope you will share such type of impressive contents again with us so that we can utilize it and get more advantage. pornmoviesjoy
Thursday, August 23, 2018 · reply ·
muneer
As demonstrated by the name the objective is to make a hand that signifies 21. Face cards are on the whole worth 10 focuses and an ace can be worth 1 or 11 focuses, which is dependent upon you. https://www.playojo.com/en/?aname=osmedia99#!registrationModal">play ojo
Tuesday, August 28, 2018 · reply ·
MathCad Online Assignment Help
This Is Really Great Work. Thank You For Sharing Such A Useful Information Here In The Blog. http://www.mathcadhelp.com
Tuesday, September 11, 2018 · reply ·
SAS Project Online Help
I Loved The Way You Discuss The Topic Great Work Thanks For The Share Your Informative Post. https://www.sashelponline.com
Tuesday, September 11, 2018 · reply ·
panistefanin
Je vous remercie de l'information! Je cherchais et ne pouvait pas trouver. Vous me aidé! http://percentagecalculator.wiki/
Tuesday, October 2, 2018 · reply ·
voyance gratuite en ligne
This is extremely fascinating substance! I have completely delighted in perusing your focuses and have reached the conclusion that you are right about a hefty portion of them. You are extraordinary.  http://voyance-amour-eternel.com/">voyance gratuite en ligne
Sunday, October 7, 2018 · reply ·
listen to music
This is an awesome blog. You provide very useful data. https://medium.com/@mahfug50/enlighten-your-mood-by-listening-to-audio-%C7%80-mp3-songs-d47cc2a9683 listen to music
Sunday, October 21, 2018 · reply ·
home
Thanks For sharing this information. It’s Nice..!!! https://games.lol/
Thursday, October 25, 2018 · reply ·
james
its great post and help me alot. please keep continue posting articles on your great site. https://games.lol/
Thursday, October 25, 2018 · reply ·
Accounting Assignment Help
This Is Really Great Work. Thank You For Sharing Such A Useful Information Here In The Blog.
Saturday, December 15, 2018 · reply ·
Essay Writing Service
This Is Really Great Work. Thank You For Sharing Such A Good And Useful Information Here In The Blog For Students. https://www.termpaperwriting.services
Saturday, December 15, 2018 · reply ·
Help With Homework
They Are Not Able To Finish The Writing Assignments On Time. For Some Students, Writing Any Writing Assignments Is Able To Waste Their Time https://assignmenthelp.xyz/
Saturday, December 15, 2018 · reply ·
Homework Help United States | AssignmentInc.com
John Arnold Is An Academic Writer Of The Dissertation-Guidance. Who Writes Quality Academic Papers For Students To Help Them In Accomplishing Their Goals. https://www.assignmentinc.com
Saturday, December 15, 2018 · reply ·
Business Setup in Dubai Freezone
We Also Share Some Information About Our Business https://uaefreezone.xyz/
Saturday, December 15, 2018 · reply ·
Mesothelioma Lawyers And Asbestos Attorneys
Science Channel’s Are Giving A Complete Knowledge To Its Viewers About Every Thing Students Write Done Dissertation On This Subjects And Show Its Importance. https://mesotheliomaasbestoshub.xyz/
Saturday, December 15, 2018 · reply ·
Coursework Assignment Help Assistance
Thanks A Lot For The Post. It Has Helped Me Get Some Nice Ideas. I Hope I Will See Some Really Good Result Soon. https://www.courseworkhelponline.xyz/
Saturday, December 15, 2018 · reply ·
ebook writing 
Good Way Of Telling, Good Post To Take Facts Regarding My Presentation Subject Matter, Which I Am Going To Deliver In My College https://www.contentastic.com/
Saturday, December 15, 2018 · reply ·
Essay Writing Service
This Is Really Great Work. Thank You For Sharing Such A Useful Information Here In The Blog. https://www.dooessay.com/
Saturday, December 15, 2018 · reply ·
Dissertation Writers
We Also Share Some Information About Our Business https://www.dissertationinc.com/
Saturday, December 15, 2018 · reply ·
Homework Help
Hi Buddy, Your Blog' S Design Is Simple And Clean And I Like It. Your Blog Posts About Online Dissertation Help Are Superb. Please Keep Them Coming. Greets!! https://www.homeworkaustralia.com/
Saturday, December 15, 2018 · reply ·
Case Study Help
I Personally Like Your Post, You Have Shared Good Article. It Will Help Me In Great Deal. https://caseassistant.xyz
Saturday, December 15, 2018 · reply ·
Our Service
I Am So Happy To Read This. This Is The Kind Of Manual That Needs To Be Given And Not The Random Misinformation That's At The Other Blogs. https://casestudysolution.xyz
Saturday, December 15, 2018 · reply ·
rough emerald
Panjshir Emerald also known as Afghanistan Emerald is one of the finest emeralds that is found in the Panjshir valley of Afghanistan which is known as the valley of the Five Lions. We offer cherry-picked investment grade emeralds from the world's favorite mine located in Panjshir. http://emerald.af/"> rough emerald
Tuesday, December 25, 2018 · reply ·
rough emerald
Panjshir Emerald also known as Afghanistan Emerald is one of the finest emeralds that is found in the Panjshir valley of Afghanistan which is known as the valley of the Five Lions. We offer cherry-picked investment grade emeralds from the world's favorite mine located in Panjshir. http://emerald.af/ rough emerald
Tuesday, December 25, 2018 · reply ·
alanabui
Your sharing is great. I am they are a lot of things from your article. That is a very interesting experience https://wuxiaworld.world
Wednesday, March 13, 2019 · reply ·
imo download
https://imodownloading.com/ https://imodownloading.com/imo-android/ Now click on the icon shown in the searched list to download and install the app.
Wednesday, March 27, 2019 · reply ·

about.me.

Torsten WeggenMy name is Torsten Weggen and I am CEO of indisoftware GmbH in Hanover, Germany. I'm into DNN since 2008. Before this, I did a lot of desktop stuff mainly coded with Visual Foxpro (see http://www.auktionsbuddy.de). 

I'm programmer, husband, father + born in 1965.

Please feel free to contact me if you have questions.

Latest Posts

DNN module development with Angular 2+ (Part 7)
6/10/2018 1:43 PM | Torsten Weggen
DNN module development with AngularJS (Part 6)
12/16/2016 7:00 AM | Torsten Weggen
DNN module development with AngularJS (Part 5)
12/16/2016 6:00 AM | Torsten Weggen
DNN module development with AngularJS (Part 4)
12/16/2016 5:00 AM | Torsten Weggen
DNN module development with AngularJS (Part 3)
12/16/2016 4:00 AM | Torsten Weggen
DNN module development with AngularJS (Part 2)
12/16/2016 3:00 AM | Torsten Weggen
DNN module development with AngularJS (Part 1)
12/15/2016 7:19 AM | Torsten Weggen
Blogging in DNN with Markdown Monster by Rick Strahl
11/27/2016 1:14 PM | Torsten Weggen
Creating a global token engine
11/18/2016 10:25 AM | Torsten Weggen
DnnImagehandler - Hot or not ?
2/21/2015 11:52 PM | Torsten Weggen

My Twitter

Torsten Weggen 3/17/2019

You can make a real difference in Abir ’s life. Join me on @Kiva https://t.co/NlCTgIAZAN

Torsten Weggen 2/2/2019

As a freelance developer I often get invites from headhunters. This one should be very interesting... https://t.co/CNrEXBTBuJ

Torsten Weggen 10/11/2018

Evernote or OneDrive user and love #markdown #markdownmonster ? Take a look at Joplin! https://t.co/4bzkiD6CHn