سؤال

انا تطوير متعدد اللغات البرنامج.بقدر رمز التطبيق يذهب ، localizability ليست قضية.يمكننا استخدام لغة محددة الموارد لدينا جميع أنواع من الأدوات التي تعمل بشكل جيد معهم.

ولكن ما هو أفضل نهج في تحديد متعدد اللغات مخطط قاعدة البيانات?دعونا نقول لدينا الكثير من الجداول (100 أو أكثر) ، وكل الجدول يمكن أن يكون العديد من الأعمدة التي يمكن أن تكون مترجمة (أكثر من nvarchar الأعمدة يجب أن تكون القابلة للترجمة).فعلى سبيل المثال أحد الجداول قد عقد معلومات المنتج:

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 الباحث FK, languagecode varchar pr_name النص ، pr_descr النص)

هذه الطريقة إذا كان لديك متعددة للترجمة العمود وهذا لا يتطلب أكثر من واحد الانضمام إلى الحصول عليه + منذ كنت لا autogenerating على translationid قد يكون من الأسهل إلى استيراد العناصر جنبا إلى جنب مع ذات الترجمات.

الجانب السلبي من هذا هو أنه إذا كان لديك مجمع اللغة تراجع آلية قد تحتاج إلى تنفيذ ذلك في كل ترجمة الجدول - إذا كنت تعتمد على بعض الإجراء المخزن إلى فعل ذلك.إذا كنت تفعل ذلك من هذا التطبيق قد لا يكون مشكلة.

اسمحوا لي أن أعرف ما كنت أعتقد - وأنا أيضا على وشك اتخاذ قرار بشأن هذه التطبيق التالي.حتى الآن لدينا تستخدم الخاص بك 3 نوع.

نصائح أخرى

هذه قضية مثيرة للاهتمام ، لذلك دعونا نيكرومانسي.

دعونا نبدأ من خلال مشاكل الأسلوب 1:
المشكلة:أنت denormalizing لتوفير السرعة.
في SQL (باستثناء كيو مع 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 

مما يعني أنك يجب أن تغير جميع الاستفسارات الخاصة بك إذا قمت بإضافة لغة جديدة.وهذا يؤدي بطبيعة الحال إلى استخدام "ديناميكية 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

المشكلة مع ذلك هو
أ) تاريخ-التنسيق جدا بلغة معينة ، حتى تحصل مشكلة هناك ، إذا لم يكن الإدخال في شكل ISO (والتي متوسط حديقة متنوعة مبرمج عادة لا تفعل ، وفي حالة تقرير المستخدم بالتأكيد لن تفعل بالنسبة لك ، حتى لو طلب صراحة أن تفعل ذلك).
و
ب) والأهم, ، تفقد أي نوع من التحقق من بناء الجملة.إذا <insert name of your "favourite" person here> يغير المخطط لأن فجأة متطلبات جناح التغيير ، ومجموعة جدول جديد إنشاء القديم اليسار ولكن الإشارة مجال إعادة تسمية, كنت لا تحصل على أي نوع من التحذير.تقرير حتى يعمل عند تشغيله دون تحديد الجناح المعلمة (==> guid.فارغة).ولكن فجأة عندما الفعلي المستخدم في الواقع يختار الجناح ==> بوم. هذا الأسلوب تماما breakes أي نوع من الاختبار.


الطريقة 2:
باختصار:"عظيم" فكرة (تحذير - السخرية) ، دعونا الجمع بين مساوئ الطريقة 3 (سرعة بطيئة عند العديد من الإدخالات) مع بالأحرى الرهيبة مساوئ الطريقة 1.
فقط الاستفادة من هذا الأسلوب هو أن تحافظ على جميع الترجمة في جدول واحد ، وبالتالي تجعل صيانة بسيطة.ولكن الشيء نفسه لا يمكن أن يتحقق مع الأسلوب 1 و ديناميكية SQL الإجراء المخزن, و (ربما مؤقتا) الجدول الذي يحتوي على ترجمة اسم الجدول الهدف (و هو بسيط جدا على افتراض انك اسمه كافة النص الخاص بك-حقول نفس).


الطريقة 3:
جدول واحد لجميع الترجمات:العيب:لديك لتخزين ن المفاتيح الخارجية في الجدول "منتجات" ن الحقول التي تريد ترجمتها.ولذلك عليك أن تفعل ن ينضم ن الميادين.عندما الترجمة الجدول العالمية ، هناك العديد من الإدخالات وينضم تصبح بطيئة.أيضا, يجب عليك دائما أن الانضمام إلى T_TRANSLATION الجدول مرات n n المجالات.هذا هو تماما النفقات العامة.الآن, ماذا تفعل عندما يجب أن تستوعب ترجمات مخصصة لكل العملاء ؟ سيكون لديك لإضافة آخر 2x ن ينضم على جدول إضافي.إذا كان لديك للانضمام ، يقول 10 الجداول مع 2x2xn = 4n إضافية ينضم, ما هذه الفوضى !أيضا هذا التصميم يجعل من الممكن استخدام نفس الترجمة مع 2 الجداول.إذا قمت بتغيير اسم البند في جدول واحد, هل حقا تريد تغيير إدخال في جدول آخر كما في كل مرة ؟

بالإضافة إلى أنك لا تستطيع حذف وإعادة إدراج الجدول بعد الآن لأن هناك الآن المفاتيح الخارجية في الجدول المنتج(ق)...يمكنك بالطبع تجاهل وضع FKs ثم <insert name of your "favourite" person here> يمكن حذف الجدول ، وإعادة إدراج كافة الإدخالات مع newid() [أو عن طريق تحديد الهوية في إدراج ، ولكن وجود الهوية-إدراج قبالة] و أن (و) يؤدي إلى البيانات القمامة (null مرجع الاستثناءات) قريبا حقا.


الأسلوب 4 (غير المدرجة):تخزين جميع اللغات في XML الحقل في قاعدة البيانات.هـ.ز

-- 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 ('دي', 'AR', الخ.) كما الابتدائية-مفتاح اللغة الجدول, ثم لا يجب البحث عن معرف اللغة.ولكن إذا كنت تفعل ذلك, ربما كنت ترغب في استخدام IETF-اللغة الوسم بدلا من ذلك, وهو أفضل, لأن تحصل دي-CH و دي-دي-وهو ليس نفس ortography الحكيم (double s بدلا من ß في كل مكان) ، على الرغم من انها نفس قاعدة اللغة.هذا فقط القليل من التفاصيل التي قد تكون مهمة بالنسبة لك ، خصوصا وأن en-US و en-GB/en-CA/en-AU أو fr-FR/fr-CA له قضايا مماثلة.
اقتباس:نحن لا نحتاج إلى ذلك ، نحن فقط لدينا برامج في اللغة الإنجليزية.
الجواب:نعم - ولكن أي واحد ??

على أي حال, إذا كنت تستخدم ID صحيح أنت مرنة ويمكن تغيير الأسلوب الخاص بك في أي وقت لاحق.
و يجب عليك استخدام هذا صحيح, لأنه لا يوجد شيء أكثر إزعاجا المدمرة ومزعجة مما فاشلة Db التصميم.

انظر أيضا RFC 5646, ISO 639-2,

و, إذا كنت لا تزال قائلا "نحن" فقط جعل التطبيق لدينا "فقط واحد الثقافة" (مثل en-US عادة)- لذلك أنا لست بحاجة إلى ذلك اضافية صحيح, هذا سيكون الوقت المناسب و المكان ذكر IANA علامات اللغة, أليس كذلك ؟
لأنها يذهب مثل هذا:

de-DE-1901
de-DE-1996

و

de-CH-1901
de-CH-1996

(كان هناك الإملاء الإصلاح في عام 1996...) حاول العثور على الكلمة في القاموس إذا كان بها أخطاء إملائية;هذا يصبح من المهم جدا في التطبيقات التعامل مع القانونية والخدمة العامة البوابات.
والأهم من ذلك أن هناك مناطق التي تتغير من السيريلية إلى الأبجدية اللاتينية التي قد تكون أكثر إزعاجا من سطحية ازعاج من بعض غامضة الإملاء الإصلاح ، ولهذا قد يكون هذا أحد الاعتبارات الهامة جدا ، اعتمادا على البلد الذي تعيش في.طريقة واحدة أو أخرى, فمن الأفضل أن يكون هذا صحيح هناك ، فقط في حالة...

تحرير:
و من خلال إضافة ON DELETE CASCADE بعد

REFERENCES dbo.T_Products( PROD_Id )

هل يمكن القول ببساطة: DELETE FROM T_Products, و على أي مفتاح خارجي انتهاك.

أما عن ترتيب سأفعل مثل هذا:

أ) يكون الخاصة بك الدال
ب) حفظ المطلوب ترتيب الاسم في اللغة الجدول

قد ترغب في وضع الترتيب في الجدول الخاص بهم ، على سبيل المثال:

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

ج) جمع اسم المتاحة في المصادقة.المستخدم.لغة المعلومات

د) كتابة SQL الخاصة بك مثل هذا:

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

ORDER BY GroupName COLLATE {#COLLATION}

هـ) ثم يمكنك أن تفعل هذا في الدال:

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

الخيار الثالث هو الأفضل لعدة أسباب:

  • لا يتطلب تغيير مخطط قاعدة البيانات لغات جديدة (وبالتالي الحد من تغييرات في التعليمات البرمجية)
  • لا تتطلب الكثير من الفضاء دون تنفيذ اللغات أو الترجمة من عنصر معين
  • يوفر أكثر مرونة
  • لا ينتهي مع متفرق الجداول
  • كنت لا داعي للقلق حول 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
)

حتى تحصل على شيء مثل user39603 يشير إلى:

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'

أنا أتفق مع الموزع بشكل عشوائي.أنا لا أرى لماذا كنت في حاجة الى طاولة "الترجمة".

أعتقد أن هذا يكفي:

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