Best-practices for localizing a SQL Server (2005/2008) database

后端 未结 10 1167
执笔经年
执笔经年 2020-12-13 11:09

Question

I\'m sure many of you have been faced by the challenge of localizing a database backend to an application. If you\'ve not then I\'d be pretty confident in

相关标签:
10条回答
  • 2020-12-13 11:52

    Here some thoghts on the Rick Strahl's blog:

    Localization of database Localization of JavaScript

    I do prefer to use a single switch in a UserSetting table , which is used by calling stored procedure ... here some of the code

    CREATE TABLE [dbo].[Lang_en_US_Msg](
        [MsgId] [int] IDENTITY(1,1) NOT NULL,
        [MsgKey] [varchar](200) NOT NULL,
        [MsgTxt] [varchar](2000) NOT NULL,
        [MsgDescription] [varchar](2000) NOT NULL,
     CONSTRAINT [PK_Lang_US-us__Msg] PRIMARY KEY CLUSTERED 
    (
        [MsgId] ASC
    )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    
    GO
    
    CREATE TABLE [dbo].[User](
        [UserId] [int] IDENTITY(1,1) NOT NULL,
        [FirstName] [varchar](50) NOT NULL,
        [MiddleName] [varchar](50) NULL,
        [LastName] [varchar](50) NULL,
        [DomainName] [varchar](50) NULL,
     CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED 
    (
        [UserId] ASC
    )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    
    CREATE TABLE [dbo].[UserSetting](
        [UserSettingId] [int] IDENTITY(1,1) NOT NULL,
        [UserId] [int] NOT NULL,
        [CultureInfo] [varchar](50) NOT NULL,
        [GuiLanguage] [varchar](10) NOT NULL,
     CONSTRAINT [PK_UserSetting] PRIMARY KEY CLUSTERED 
    (
        [UserSettingId] ASC
    )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    

    GO

     ALTER TABLE [dbo].[UserSetting] ADD  CONSTRAINT [DF_UserSetting_CultureInfo]  DEFAULT ('fi-FI') FOR [CultureInfo]
     GO
    
     CREATE TABLE [dbo].[Lang_fi_FI_Msg](
        [MsgId] [int] IDENTITY(1,1) NOT NULL,
        [MsgKey] [varchar](200) NOT NULL,
        [MsgTxt] [varchar](2000) NOT NULL,
        [MsgDescription] [varchar](2000) NOT NULL,
        [DbSysNameForExpansion] [varchar](50) NULL,
     CONSTRAINT [PK_Lang_Fi-fi__Msg] PRIMARY KEY CLUSTERED 
    (
        [MsgId] ASC
    )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    
    CREATE PROCEDURE [dbo].[procGui_GetPageMsgs]
    @domainUser varchar(50) ,           -- the domain_user performing the action  
    @msgOut varchar(4000) OUT,        -- the (error) msg to be shown to the user   
    @debugMsgOut varchar(4000) OUT ,   -- this variable holds the debug msg to be shown if debug level is enabled   
    @ret int OUT                  -- the variable indicating success or failure 
    
    AS                            
    BEGIN -- proc start                            
     SET NOCOUNT ON;                            
    
    declare @procedureName varchar(200)        
    declare @procStep varchar(4000)  
    
    
    set @procedureName = ( SELECT OBJECT_NAME(@@PROCID))        
    set @msgOut = ' '     
    set @debugMsgOut = ' '     
    set @procStep = ' '     
    
    
    BEGIN TRY        --begin try                  
    set @ret = 1 --assume false from the beginning                  
    
    --===============================================================
     --debug   set @procStep=@procStep + 'GETTING THE GUI LANGUAGE FOR THIS USER '
    --===============================================================
    
    declare @guiLanguage nvarchar(10)
    
    
    
    
    if ( @domainUser is null)
        set @guiLanguage = (select Val from AppSetting where Name='guiLanguage')
    else 
        set @guiLanguage = (select GuiLanguage from UserSetting us join [User] u on u.UserId = us.UserId where u.DomainName=@domainUser)
    
    set @guiLanguage = REPLACE ( @guiLanguage , '-' , '_' ) ;
    
    
    --===============================================================
    set @procStep=@procStep + ' BUILDING THE SQL QUERY '
    --===============================================================
    
    DECLARE @sqlQuery AS nvarchar(2000)
    SET @sqlQuery = 'SELECT  MsgKey , MsgTxt FROM dbo.lang_' + @guiLanguage + '_Msg'
    
    
    --===============================================================
    set @procStep=@procStep + 'EXECUTING THE SQL QUERY'
    --===============================================================
    print @sqlQuery
    
        exec sp_executesql @sqlQuery
    
        set @debugMsgOut = @procStep
        set @ret = @@ERROR                  
    
    
    END TRY        --end try                  
    
    BEGIN CATCH                        
     PRINT 'In CATCH block.                         
     Error number: ' + CAST(ERROR_NUMBER() AS varchar(10)) + '                        
     Error message: ' + ERROR_MESSAGE() + '                        
     Error severity: ' + CAST(ERROR_SEVERITY() AS varchar(10)) + '                        
     Error state: ' + CAST(ERROR_STATE() AS varchar(10)) + '                        
     XACT_STATE: ' + CAST(XACT_STATE() AS varchar(10));                        
    
    set @msgOut = 'Failed to execute ' + @sqlQuery             
    set @debugMsgOut = ' Error number: ' + CAST(ERROR_NUMBER() AS varchar(10)) +               
     'Error message: ' + ERROR_MESSAGE() + 'Error severity: ' + CAST(ERROR_SEVERITY() AS varchar(10)) +               
     'Error state: ' + CAST(ERROR_STATE() AS varchar(10)) + 'XACT_STATE: ' + CAST(XACT_STATE() AS varchar(10))                        
    
    --record the error in the database                        
    --debug    
     --EXEC [dbo].[procUtils_DebugDb]
        --  @DomainUser = @domainUser,
        --  @debugmsg = @debugMsgOut,
        --  @ret = 1,
        --  @procedureName = @procedureName ,
        --  @procedureStep = @procStep
    
     -- set @ret = 1                       
    
    END CATCH                        
    
    
    return  @ret                                   
    END --procedure end                             
    
    0 讨论(0)
  • 2020-12-13 11:54

    I like the XML approach, because the separate-table-solution would NOT return a result if e.g. there is no swedish translation (cultureID = 1) unless you do an outer join. But nevertheless you can NOT fall back to English. With the XML approach you simply can fall back to English. Any news on the XML approach in a producitve environment?

    0 讨论(0)
  • 2020-12-13 11:57

    I see the delima overall - you have a single entity you must represent as a single instance (one ProductID of "10" for example), but have multiple localized text of different columns/properties. That is a tough one, and I do see the need for POS systems, that you only want to track that one ProductID = 10, not multiple products that have different ProductIDs, but are the same thing with just different text.

    I would lean towards the XML column solution you and others have outlined here already. Yes, it's more data transfering over the wire - but, it keeps things simple and can be filtered with XElement if packet site becomes an issue.

    The main drawback being the amount of data transfered over the wire from the DB to the service layer/UI/App. I would try to do some transformation on the SQL end before returning the result, to only return the one culture UI. You could always just SELECT the currect culsture via xml in an sproc, and return it as normal text as well.

    Overall, this is different then, say, a Blog Post or CMS need for localization - which I've done a few of.

    My approach to the Post scenerio would be similar to TToni's, with the exception of modelling the data from a the Domain's perspective (and a touch of BDD). With that said, focus on what you want to achieve:

    Given a users culture is "sv-se"
    When the user views a post list
    It should list posts only in "sv-se" culture
    

    This means that the user should see a list of posts only for their culture. The way we implemented this before was to pass in a set of cultures to query for based on what the user could see. If the user has 'sv-se' set as their primary, but also has selected they speak US English (en-us), then the query would be:

    SELECT * FROM Post WHERE CultureUI IN ('sv-se', 'en-us')
    

    Notice how this gives you all posts and their different PostID, unique to that language. The PostID isn't as important here on blogs because each post is bound to a different language. If there are copies being transcribed, that works fine here too as each post is unique to that culture, and therefore gets a unique set of comments and such.

    But to go back to the 1st part of my answer, your need stems from the requirement of needing a single instance with multiple texts. An Xml column fits that fine.

    0 讨论(0)
  • 2020-12-13 11:58

    Indexing becomes an issue. I don't think you can index xml, and of course, you can't index it if you store it as a string because every string will start with <localization> <text culture="...">.

    0 讨论(0)
提交回复
热议问题