문제

내가 개발하는 다국어 소프트웨어입니다.응용 프로그램 코드를 간,지역화 가능성이 문제가 되지 않습니다.우리가 사용할 수 있어 특정 자원 및 모든 종류의 도구로 작동하는 그들.

하지만 가장 좋은 방법은 무엇입에 정의는 다국어 데이터베이스에 스키마는?자의 말을 우리는 많은 테이블(100 개 이상),그리고 각각의 테이블에 여러 개 있을 수 있습할 수 있는 열화(대부분의 이며 열어야 한 지역화 가능).예를 들어 하나의 테이블을 유지할 수도 있습 제품 정보:

CREATE TABLE T_PRODUCT (
  NAME        NVARCHAR(50),
  DESCRIPTION NTEXT,
  PRICE       NUMBER(18, 2)
)

나는 생각할 수 있는 세 가지 방법을 지원하는 다국어 구사 가능 텍스트 이름 및 설명에 열:

  1. 별도의 열을 위해 각 언어

    때 우리는 새로운 언어를 추가하는 시스템,우리가 만들어야 합니다 추가 열을 저장하는 번역 텍스트,이와 같은:

    CREATE TABLE T_PRODUCT (
      NAME_EN        NVARCHAR(50),
      NAME_DE        NVARCHAR(50),
      NAME_SP        NVARCHAR(50),
      DESCRIPTION_EN NTEXT,
      DESCRIPTION_DE NTEXT,
      DESCRIPTION_SP NTEXT,
      PRICE          NUMBER(18,2)
    )
    
  2. 번역된 테이블에 대한 열 각 언어

    를 저장하는 대신 번역 텍스트만 외국인 핵심 번역 테이블에 저장됩니다.번역 테이블을 포함한 언어입니다.

    CREATE TABLE T_PRODUCT (
      NAME_FK        int,
      DESCRIPTION_FK int,
      PRICE          NUMBER(18, 2)
    )
    
    CREATE TABLE T_TRANSLATION (
      TRANSLATION_ID,
      TEXT_EN NTEXT,
      TEXT_DE NTEXT,
      TEXT_SP NTEXT
    )
    
  3. 번역 테이블과 함께 행해 각 언어

    를 저장하는 대신 번역 텍스트만 외국인 핵심 번역 테이블에 저장됩니다.번역 테이블을 포함하는 유일한 열쇠 및 별도의 테이블을 포함한 각각의 번역하는 언어입니다.

    CREATE TABLE T_PRODUCT (
      NAME_FK        int,
      DESCRIPTION_FK int,
      PRICE          NUMBER(18, 2)
    )
    
    CREATE TABLE T_TRANSLATION (
      TRANSLATION_ID
    )
    
    CREATE TABLE T_TRANSLATION_ENTRY (
      TRANSLATION_FK,
      LANGUAGE_FK,
      TRANSLATED_TEXT NTEXT
    )
    
    CREATE TABLE T_TRANSLATION_LANGUAGE (
      LANGUAGE_ID,
      LANGUAGE_CODE CHAR(2)
    )
    

장점과 단점이 있습니다 각 솔루션,그리고 나는 무엇을 알고 싶은 당신의 경험을 가진 이러한 방식을,당신은 어떻게 추천하고 어떻게 갈 것에 대해 설계하는 다국어 데이터베이스에 스키마.

도움이 되었습니까?

해결책

각 번역 가능한 테이블에 대한 관련 번역 테이블이있는 것에 대해 어떻게 생각하십니까?

테이블 생성 t_product (pr_id int, 가격 번호 (18, 2))

테이블 만들기 t_product_tr (pr_id int fk, languagecode varchar, pr_name text, pr_descr 텍스트)

이 방법으로 여러 번 번역 가능한 열이 있다면 +를 얻으려면 단일 조인 만 있으면 + 번역을 자동 생성하지 않기 때문에 관련 번역과 함께 항목을 가져 오는 것이 더 쉬울 수 있습니다.

이것의 부정적인 측면은 복잡한 언어 폴백 메커니즘이있는 경우 각 번역 테이블에 대해이를 구현해야 할 수도 있다는 것입니다. 앱에서 그렇게하면 문제가되지 않을 것입니다.

당신의 생각을 알려주세요 - 나는 또한 다음 응용 프로그램을 위해 이것에 대해 결정하려고합니다. 지금까지 우리는 당신의 세 번째 유형을 사용했습니다.

다른 팁

이것은 흥미로운 문제이므로,자 necromance.

해서 시작하자는 문제의 방법 1:
문제:당신이 비정규화하여 저장 속도입니다.
에서 SQL 제외하고(PostGreSQL 와 hstore),할 수 없습니다 통과 매개 변수는 언어와 말:

SELECT ['DESCRIPTION_' + @in_language]  FROM T_Products

그래서 당신은이 작업을 수행 할 수 있습니다:

SELECT 
    Product_UID 
    ,
    CASE @in_language 
        WHEN 'DE' THEN DESCRIPTION_DE 
        WHEN 'SP' THEN DESCRIPTION_SP 
        ELSE DESCRIPTION_EN 
    END AS Text 
FROM T_Products 

는 것을 의미가 있을 변경하는 모든 쿼리를 추가하는 경우 새로운 언어입니다.이 자연적으로 지도를 사용하여"dynamic SQL",그래서 당신은 없을 변경하는 귀하의 모든 쿼리를 처리합니다.

이것은 일반적으로 무언가가 다음과 같이(그리고 그것을 사용할 수 없습망 또는 테이블을 반환 함수에 의해 방법으로,진짜 문제는 경우에 당신은 실제로 필터링 할 필요가 보고기간말 현재 연결)

CREATE PROCEDURE [dbo].[sp_RPT_DATA_BadExample]
     @in_mandant varchar(3) 
    ,@in_language varchar(2) 
    ,@in_building varchar(36) 
    ,@in_wing varchar(36) 
    ,@in_reportingdate varchar(50) 
AS
BEGIN
    DECLARE @sql varchar(MAX), @reportingdate datetime

    -- Abrunden des Eingabedatums auf 00:00:00 Uhr
    SET @reportingdate = CONVERT( datetime, @in_reportingdate) 
    SET @reportingdate = CAST(FLOOR(CAST(@reportingdate AS float)) AS datetime)
    SET @in_reportingdate = CONVERT(varchar(50), @reportingdate) 

    SET NOCOUNT ON;


    SET @sql='SELECT 
         Building_Nr AS RPT_Building_Number 
        ,Building_Name AS RPT_Building_Name 
        ,FloorType_Lang_' + @in_language + ' AS RPT_FloorType 
        ,Wing_No AS RPT_Wing_Number 
        ,Wing_Name AS RPT_Wing_Name 
        ,Room_No AS RPT_Room_Number 
        ,Room_Name AS RPT_Room_Name 
    FROM V_Whatever 
    WHERE SO_MDT_ID = ''' + @in_mandant + ''' 

    AND 
    ( 
        ''' + @in_reportingdate + ''' BETWEEN CAST(FLOOR(CAST(Room_DateFrom AS float)) AS datetime) AND Room_DateTo 
        OR Room_DateFrom IS NULL 
        OR Room_DateTo IS NULL 
    ) 
    '

    IF @in_building    <> '00000000-0000-0000-0000-000000000000' SET @sql=@sql + 'AND (Building_UID  = ''' + @in_building + ''') '
    IF @in_wing    <> '00000000-0000-0000-0000-000000000000' SET @sql=@sql + 'AND (Wing_UID  = ''' + @in_wing + ''') '

    EXECUTE (@sql) 

END


GO

이 문제
a)일 형식은 매우 특정 언어,그래서 당신은 문제가 당신이 입력하지 않는 ISO 형식(평균원-다양한 프로그래머는 일반적으로 하지 않고의 경우에 보고서 사용자가 지옥으로 확인을 하지 않을 것은 당신을 위해 경우에도 명시적으로 지시하도).

b) 가장 크게, 신 느슨한 모든 종류의 구문 검사.는 경우 <insert name of your "favourite" person here> 바꾸기 때문에 스키마 갑자기 요구 사항에 대한 날개 변경,그리고 새로운 테이블을 만들면 오래된 왼쪽 그러나 참조 필드 이름이 바뀌지 않는 모든 종류의 경고입니다.보고서 심지어 작동 당신이 그것을 실행할 때를 선택하지 않고 날개를 매개 변수 (==>입니다.빈).하지만 갑자기면 실제 사용자가 실제적으로 선택한 날개==> . 이 방법은 완전히 breakes 모든 종류의 테스트합니다.


방법 2:
간단히 말해서:"좋은"아이디어(경고-풍자),하자의 결합의 단점은 방법을 3(느린 경우 속도 많은 항목)이라는 끔찍한 방법의 단점 1.
만 이 방법의 장점을 모두 유지역에서 한 테이블,따라서 유지보수 간단합니다.그러나,동일한 것으로 달성될 수 있습 방법 1dynamic SQL 저장 프로시저는(아마도 임시)테이블을 포함하는 번역의 이름을 목표 테이블(고은 매우 간단하고 가정하고 당신은 당신이라는 귀하의 모든 텍스트 필드 동일합니다).


방법 3:
한 테이블에 대한 모든 번역:단점:저장해야 합니다 n 키 외국에서 제품에 대한 테이블 n 할 필드를 번역합니다.따라서,당신은 당신이해야 할 n 조인을 위한 필드가 있습니다.을 때 번역 테이블은 세계,그것은 많은 항목,조인 속도가 느려집니다.또한,당신은 항상 가입 T_TRANSLATION 테이블의 n 번을 위한 필드가 있습니다.이것은 아주 오버헤드가 발생합니다.지금,당신은 무엇을해야 하는 경우에 맞게 사용자 정의 번역별 고객?당신은 다른 추가 2 배 n 인에 추가적인 테이블.이 있는 경우,가입 말하는 10 가지 테이블과,2x2xn=4n 추가적인 조인 것이다.또한,이 디자인은 사용하는 것을 가능하게 합니다 동일한 번역으로 2 개의 테이블이 있습니다.변경하는 경우 항목 이름에서 한 테이블,내가 정말 원하는 항목을 변경 다른 테이블에서뿐만 아니라 매 시간?

플러스 당신은 당신을 삭제할 수 없습니다가 다시 삽입하 테이블,더 이상이 있기 때문에 지금 키 외국에서 제품이블(s)...할 수 있습의 과정을 생략 설정 FKs,다음 <insert name of your "favourite" person here> 삭제할 수 있습니다 테이블,그리고 다시 삽입하는 모든 항목 십시오() [거나 지정하는 id 를 삽입이지만,가 id-삽입 OFF],그리고 그(것)리드 데이터-쓰레기(null 을 참조하는 예외)정말 빨리.


방법 4(나):저장의 모든 언어로에서는 XML 데이터베이스에 있는 필드.e.g

-- CREATE TABLE MyTable(myfilename nvarchar(100) NULL, filemeta xml NULL )


;WITH CTE AS 
(
      -- INSERT INTO MyTable(myfilename, filemeta) 
      SELECT 
             'test.mp3' AS myfilename 
            --,CONVERT(XML, N'<?xml version="1.0" encoding="utf-16" standalone="yes"?><body>Hello</body>', 2) 
            --,CONVERT(XML, N'<?xml version="1.0" encoding="utf-16" standalone="yes"?><body><de>Hello</de></body>', 2) 
            ,CONVERT(XML
            , N'<?xml version="1.0" encoding="utf-16" standalone="yes"?>
<lang>
      <de>Deutsch</de>
      <fr>Français</fr>
      <it>Ital&amp;iano</it>
      <en>English</en>
</lang>
            ' 
            , 2 
            ) AS filemeta 
) 

SELECT 
       myfilename
      ,filemeta
      --,filemeta.value('body', 'nvarchar') 
      --, filemeta.value('.', 'nvarchar(MAX)') 

      ,filemeta.value('(/lang//de/node())[1]', 'nvarchar(MAX)') AS DE
      ,filemeta.value('(/lang//fr/node())[1]', 'nvarchar(MAX)') AS FR
      ,filemeta.value('(/lang//it/node())[1]', 'nvarchar(MAX)') AS IT
      ,filemeta.value('(/lang//en/node())[1]', 'nvarchar(MAX)') AS EN
FROM CTE 

다음 얻을 수 있습니다 값에 의해 XPath-에서 SQL 쿼리를 넣을 수 있습니다 문자열 변수

filemeta.value('(/lang//' + @in_language + '/node())[1]', 'nvarchar(MAX)') AS bla

과 값을 업데이트할 수 있습 다음과 같다:

UPDATE YOUR_TABLE
SET YOUR_XML_FIELD_NAME.modify('replace value of (/lang/de/text())[1] with "&quot;I am a ''value &quot;"')
WHERE id = 1 

할 수 있는 대체 /lang/de/...'.../' + @in_language + '/...'

의 종류는 다음과 같은 PostGre hstore 를 제외하고,그로 인한 오버헤드를 분석 XML(읽는 대신 항목에서는 연관 배열에 PG hstore)가 너무 느리스 xml 인코딩것은 너무 고통스러운하는 것은 도움이 될 수 있습니다.


방법 5(권장 SunWuKung,당신이 하나를 선택해야):하나 번역 테이블에 대한 각각의"상품"이다.즉 하나의 행당 언어,그리고 여러 가지""텍스트 필드,필요 그래서 하나의(왼쪽)가입하세 N 니다.당신은 쉽게 추가할 수 있습니다 기본 필드에서"제품"-테이블,당신은 쉽게 삭제하고 다시 삽입하면 번역 테이블을 만들 수 있습니다 두 번째 테이블에 대한 주문 번역서(요청 시)에는 삭제할 수도 있습니다 다시 삽입),그리고 당신은 여전히 모든 외국 키를 사용합니다.

보는 사람을 사랑하는 사람들의 모범이 작동:

첫째,테이블을 만들:

CREATE TABLE dbo.T_Languages
(
     Lang_ID int NOT NULL
    ,Lang_NativeName national character varying(200) NULL
    ,Lang_EnglishName national character varying(200) NULL
    ,Lang_ISO_TwoLetterName character varying(10) NULL
    ,CONSTRAINT PK_T_Languages PRIMARY KEY ( Lang_ID )
);

GO




CREATE TABLE dbo.T_Products
(
     PROD_Id int NOT NULL
    ,PROD_InternalName national character varying(255) NULL
    ,CONSTRAINT PK_T_Products PRIMARY KEY ( PROD_Id )
); 

GO



CREATE TABLE dbo.T_Products_i18n
(
     PROD_i18n_PROD_Id int NOT NULL
    ,PROD_i18n_Lang_Id int NOT NULL
    ,PROD_i18n_Text national character varying(200) NULL
    ,CONSTRAINT PK_T_Products_i18n PRIMARY KEY (PROD_i18n_PROD_Id, PROD_i18n_Lang_Id)
);

GO

-- ALTER TABLE dbo.T_Products_i18n  WITH NOCHECK ADD  CONSTRAINT FK_T_Products_i18n_T_Products FOREIGN KEY(PROD_i18n_PROD_Id)
ALTER TABLE dbo.T_Products_i18n  
    ADD CONSTRAINT FK_T_Products_i18n_T_Products 
    FOREIGN KEY(PROD_i18n_PROD_Id)
    REFERENCES dbo.T_Products (PROD_Id)
ON DELETE CASCADE 
GO

ALTER TABLE dbo.T_Products_i18n CHECK CONSTRAINT FK_T_Products_i18n_T_Products
GO

ALTER TABLE dbo.T_Products_i18n 
    ADD  CONSTRAINT FK_T_Products_i18n_T_Languages 
    FOREIGN KEY( PROD_i18n_Lang_Id )
    REFERENCES dbo.T_Languages( Lang_ID )
ON DELETE CASCADE 
GO

ALTER TABLE dbo.T_Products_i18n CHECK CONSTRAINT FK_T_Products_i18n_T_Products
GO



CREATE TABLE dbo.T_Products_i18n_Cust
(
     PROD_i18n_Cust_PROD_Id int NOT NULL
    ,PROD_i18n_Cust_Lang_Id int NOT NULL
    ,PROD_i18n_Cust_Text national character varying(200) NULL
    ,CONSTRAINT PK_T_Products_i18n_Cust PRIMARY KEY ( PROD_i18n_Cust_PROD_Id, PROD_i18n_Cust_Lang_Id )
);

GO

ALTER TABLE dbo.T_Products_i18n_Cust  
    ADD CONSTRAINT FK_T_Products_i18n_Cust_T_Languages 
    FOREIGN KEY(PROD_i18n_Cust_Lang_Id)
    REFERENCES dbo.T_Languages (Lang_ID)

ALTER TABLE dbo.T_Products_i18n_Cust CHECK CONSTRAINT FK_T_Products_i18n_Cust_T_Languages

GO



ALTER TABLE dbo.T_Products_i18n_Cust  
    ADD CONSTRAINT FK_T_Products_i18n_Cust_T_Products 
    FOREIGN KEY(PROD_i18n_Cust_PROD_Id)
REFERENCES dbo.T_Products (PROD_Id)
GO

ALTER TABLE dbo.T_Products_i18n_Cust CHECK CONSTRAINT FK_T_Products_i18n_Cust_T_Products
GO

그 다음에 입력 데이터

DELETE FROM T_Languages;
INSERT INTO T_Languages (Lang_ID, Lang_NativeName, Lang_EnglishName, Lang_ISO_TwoLetterName) VALUES (1, N'English', N'English', N'EN');
INSERT INTO T_Languages (Lang_ID, Lang_NativeName, Lang_EnglishName, Lang_ISO_TwoLetterName) VALUES (2, N'Deutsch', N'German', N'DE');
INSERT INTO T_Languages (Lang_ID, Lang_NativeName, Lang_EnglishName, Lang_ISO_TwoLetterName) VALUES (3, N'Français', N'French', N'FR');
INSERT INTO T_Languages (Lang_ID, Lang_NativeName, Lang_EnglishName, Lang_ISO_TwoLetterName) VALUES (4, N'Italiano', N'Italian', N'IT');
INSERT INTO T_Languages (Lang_ID, Lang_NativeName, Lang_EnglishName, Lang_ISO_TwoLetterName) VALUES (5, N'Russki', N'Russian', N'RU');
INSERT INTO T_Languages (Lang_ID, Lang_NativeName, Lang_EnglishName, Lang_ISO_TwoLetterName) VALUES (6, N'Zhungwen', N'Chinese', N'ZH');

DELETE FROM T_Products;
INSERT INTO T_Products (PROD_Id, PROD_InternalName) VALUES (1, N'Orange Juice');
INSERT INTO T_Products (PROD_Id, PROD_InternalName) VALUES (2, N'Apple Juice');
INSERT INTO T_Products (PROD_Id, PROD_InternalName) VALUES (3, N'Banana Juice');
INSERT INTO T_Products (PROD_Id, PROD_InternalName) VALUES (4, N'Tomato Juice');
INSERT INTO T_Products (PROD_Id, PROD_InternalName) VALUES (5, N'Generic Fruit Juice');

DELETE FROM T_Products_i18n;
INSERT INTO T_Products_i18n (PROD_i18n_PROD_Id, PROD_i18n_Lang_Id, PROD_i18n_Text) VALUES (1, 1, N'Orange Juice');
INSERT INTO T_Products_i18n (PROD_i18n_PROD_Id, PROD_i18n_Lang_Id, PROD_i18n_Text) VALUES (1, 2, N'Orangensaft');
INSERT INTO T_Products_i18n (PROD_i18n_PROD_Id, PROD_i18n_Lang_Id, PROD_i18n_Text) VALUES (1, 3, N'Jus d''Orange');
INSERT INTO T_Products_i18n (PROD_i18n_PROD_Id, PROD_i18n_Lang_Id, PROD_i18n_Text) VALUES (1, 4, N'Succo d''arancia');
INSERT INTO T_Products_i18n (PROD_i18n_PROD_Id, PROD_i18n_Lang_Id, PROD_i18n_Text) VALUES (2, 1, N'Apple Juice');
INSERT INTO T_Products_i18n (PROD_i18n_PROD_Id, PROD_i18n_Lang_Id, PROD_i18n_Text) VALUES (2, 2, N'Apfelsaft');

DELETE FROM T_Products_i18n_Cust;
INSERT INTO T_Products_i18n_Cust (PROD_i18n_Cust_PROD_Id, PROD_i18n_Cust_Lang_Id, PROD_i18n_Cust_Text) VALUES (1, 2, N'Orangäsaft'); -- Swiss German, if you wonder

고 다음 데이터 쿼리:

DECLARE @__in_lang_id int
SET @__in_lang_id = (
    SELECT Lang_ID
    FROM T_Languages
    WHERE Lang_ISO_TwoLetterName = 'DE'
)

SELECT 
     PROD_Id 
    ,PROD_InternalName -- Default Fallback field (internal name/one language only setup), just in ResultSet for demo-purposes
    ,PROD_i18n_Text  -- Translation text, just in ResultSet for demo-purposes
    ,PROD_i18n_Cust_Text  -- Custom Translations (e.g. per customer) Just in ResultSet for demo-purposes
    ,COALESCE(PROD_i18n_Cust_Text, PROD_i18n_Text, PROD_InternalName) AS DisplayText -- What we actually want to show 
FROM T_Products 

LEFT JOIN T_Products_i18n 
    ON PROD_i18n_PROD_Id = T_Products.PROD_Id 
    AND PROD_i18n_Lang_Id = @__in_lang_id 

LEFT JOIN T_Products_i18n_Cust 
    ON PROD_i18n_Cust_PROD_Id = T_Products.PROD_Id
    AND PROD_i18n_Cust_Lang_Id = @__in_lang_id

당신은 게으른,다음 사용할 수 있습니다 또한 ISO-TwoLetterName('DE','EN',etc.) 으로 기본 키의 언어 테이블에,당신은 없을 조회 language id.하지만 그렇게 할 경우,당신은 어쩌면 사용하고 싶 IETF-언어 태그 대신에는 것이 좋기 때문에,당신이 얻을 de-CH 과 데 드는 정말 동일하지 않습 ortography-명(더블 s 는 대신 ß 방)지만,그것은 동일한 기본 언어입니다.는 것으로 그냥 아주 작은 세부사항 중요 할 수있는 당신에게,특히 고려하는 en-US 고 en-GB/en-CA/en-AU 또는 fr-FR/fr-캘리포니아는 유사한 문제입니다.
견적:우리는 필요하지 않습니다,우리는 우리 소프트웨어는 영어입니다.
대답:그렇습니다-하지만 하나?

어쨌든,당신이 사용하는 경우 정수 ID,당신은 유연하고,귀하의 방법을 변경할 수 있습니다 나중에 시간이다.
고 사용해야 합니다 정수,아무것도 없기 때문에 더 짜증나,파괴 및 골칫거보다는 것이지 Db 디자인이다.

또한 참조 RFC5646, ISO639-2,

고,당신은 여전히 말하는"우리" 우리의 응용 프로그램를 위한"만 문화"(en-US 일반적으로)-그러므로 나는 할 필요가 없는 추가 정수,이 것은 좋은 시간과 장소를 언급하는 IANA 언어 태그, 지 않나요?
기 때문에 그들은 다음과 같이 이동:

de-DE-1901
de-DE-1996

de-CH-1901
de-CH-1996

(이 있었는 맞춤법 개혁 1996 년에는...) 찾는 시도는 단어에서 사전 경우 맞춤법이 틀린;이것은 매우 중요한 응용 프로그램에서 다루는 법적 및 공 서비스 포털입니다.
더 중요한 것은,거기 지역에서 변경 cyrillic 라틴 알파벳,수있는 것보다 더 문제의 표면 불쾌의 일부 모 맞춤법 개혁의 이유입니다,이 있는 중요한 고려사항이 너무에 따라 어느 나라에 살고 있습니다.방법 중 하나 또는 다른 것이 낫는 정수에 있는 경우에는 그냥...

편집:
신고를 하지 않음으로 인한 ON DELETE CASCADE 후에

REFERENCES dbo.T_Products( PROD_Id )

당신은 단순히 말할 수 있: DELETE FROM T_Products, 을 얻고,외국 키를 위반이 발생합니다.

으로 정렬,I'd do it like this:

A)자신의 DAL
B)을 저장하고 이름을 정렬한 언어 테이블

할 수 있습을 넣어 콜레이션에서 자신의 테이블,예를 들어:

SELECT * FROM sys.fn_helpcollations() 
WHERE description LIKE '%insensitive%'
AND name LIKE '%german%' 

C)데이터 정렬을 이름에서 사용 가능한 auth.사용자.언어보

D)작성 SQL 다음과 같다:

SELECT 
    COALESCE(GRP_Name_i18n_cust, GRP_Name_i18n, GRP_Name) AS GroupName 
FROM T_Groups 

ORDER BY GroupName COLLATE {#COLLATION}

E)그런 다음 이를 수행할 수 있습에서 당신의 달:

cmd.CommandText = cmd.CommandText.Replace("{#COLLATION}", auth.user.language.collation)

는 것입니다 그게 당신이 완벽하게 구성된 SQL 쿼리

SELECT 
    COALESCE(GRP_Name_i18n_cust, GRP_Name_i18n, GRP_Name) AS GroupName 
FROM T_Groups 

ORDER BY GroupName COLLATE German_PhoneBook_CI_AI

세 번째 옵션은 몇 가지 이유로 가장 좋습니다.

  • 새로운 언어에 대한 데이터베이스 스키마 변경이 필요하지 않으므로 코드 변경 제한)
  • 구현되지 않은 언어 또는 AA 특정 항목의 번역을위한 공간이 많이 필요하지 않습니다.
  • 가장 유연성을 제공합니다
  • 당신은 희소 테이블로 끝나지 않습니다
  • Null 키에 대해 걱정할 필요가없고 Null 항목 대신 기존 번역을 표시하고 있는지 확인합니다.
  • 다른 번역 가능한 항목/사물 등을 포함하도록 데이터베이스를 변경하거나 확장하는 경우 동일한 테이블과 시스템을 사용할 수 있습니다. 이는 나머지 데이터와 매우 결합되지 않습니다.

-아담

이 예를 살펴보십시오.

PRODUCTS (
    id   
    price
    created_at
)

LANGUAGES (
    id   
    title
)

TRANSLATIONS (
    id           (// id of translation, UNIQUE)
    language_id  (// id of desired language)
    table_name   (// any table, in this case PRODUCTS)
    item_id      (// id of item in PRODUCTS)
    field_name   (// fields to be translated)
    translation  (// translation text goes here)
)

설명 할 필요가 없다고 생각합니다. 구조는 그 자체를 묘사합니다.

나는 보통이 접근법 (실제 SQL이 아님)으로 이동합니다. 이것은 마지막 옵션에 해당합니다.

table Product
productid INT PK, price DECIMAL, translationid INT FK

table Translation
translationid INT PK

table TranslationItem
translationitemid INT PK, translationid INT FK, text VARCHAR, languagecode CHAR(2)

view ProductView
select * from Product
inner join Translation
inner join TranslationItem
where languagecode='en'

모든 번역 가능한 텍스트를 한 곳에 있으면 유지 보수가 훨씬 쉬워지기 때문입니다. 때로는 번역이 번역 기관에 아웃소싱되기도합니다.이 방법으로 하나의 큰 수출 파일 만 보내고 쉽게 가져올 수 있습니다.

기술 세부 사항과 솔루션으로 가기 전에 잠시 중지하고 요구 사항에 대해 몇 가지 질문을해야합니다. 답은 기술 솔루션에 큰 영향을 줄 수 있습니다. 그러한 질문의 예는 다음과 같습니다.
- 모든 언어가 항상 사용됩니까?
- 다른 언어 버전으로 누구와 언제 열을 채울 것인가?
- 사용자가 텍스트의 특정 언어가 필요하고 시스템에 없을 때 어떻게됩니까?
- 텍스트 만 현지화되거나 다른 항목도 있습니다 (예 : 가격은 $ 및 €에 저장 될 수 있습니다.

나는 현지화를위한 몇 가지 팁을 찾고 있었고이 주제를 발견했습니다. 이것이 왜 사용되는지 궁금합니다.

CREATE TABLE T_TRANSLATION (
   TRANSLATION_ID
)

따라서 사용자 39603과 같은 것을 얻을 수 있습니다.

table Product
productid INT PK, price DECIMAL, translationid INT FK

table Translation
translationid INT PK

table TranslationItem
translationitemid INT PK, translationid INT FK, text VARCHAR, languagecode CHAR(2)

view ProductView
select * from Product
inner join Translation
inner join TranslationItem
where languagecode='en'

당신은 단지 테이블 번역을 떠나서 이것을 얻을 수 없습니다.

    table Product
    productid INT PK, price DECIMAL

    table ProductItem
    productitemid INT PK, productid INT FK, text VARCHAR, languagecode CHAR(2)

    view ProductView
    select * from Product
    inner join ProductItem
    where languagecode='en'

나는 Randomizer에 동의합니다. 왜 "번역"테이블이 필요한지 모르겠습니다.

나는 이것으로 충분하다고 생각한다 :

TA_product: ProductID, ProductPrice
TA_Language: LanguageID, Language
TA_Productname: ProductnameID, ProductID, LanguageID, ProductName

아래 접근 방식이 실행 가능합니까? 열이 1 개 이상 번역 해야하는 테이블이 있다고 가정 해 봅시다. 따라서 제품의 경우 번역이 필요한 제품 이름과 제품 설명을 모두 가질 수 있습니다. 다음을 수행 할 수 있습니까?

CREATE TABLE translation_entry (
      translation_id        int,
      language_id           int,
      table_name            nvarchar(200),
      table_column_name     nvarchar(200),
      table_row_id          bigint,
      translated_text       ntext
    )

    CREATE TABLE translation_language (
      id int,
      language_code CHAR(2)
    )   

"가장 좋은 것"은 프로젝트 상황을 기반으로합니다. 첫 번째는 선택 및 유지 관리가 쉽고, 일부 엔티티시 테이블에 가입 할 필요가 없기 때문에 성능이 가장 좋습니다. POJECT가 2 또는 3 언어 만 지원하는 것으로 확인되면 증가하지 않으면 사용할 수 있습니다.

두 번째는 오키이지만 이해하고 유지하기가 어렵습니다. 그리고 성능은 첫 번째 성능보다 나쁩니다.

마지막은 확장 성이 좋지만 성능은 좋지 않습니다. t_translation_entry 테이블이 점점 커지고 일부 테이블에서 엔티티 목록을 검색하려는 경우 끔찍합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top