ข้อความยกเว้นเป็นภาษาอังกฤษ?


298

เรากำลังบันทึกข้อยกเว้นใด ๆ ที่เกิดขึ้นในระบบของเราโดยการเขียนข้อยกเว้นข้อความไปยังไฟล์ อย่างไรก็ตามพวกเขาเขียนในวัฒนธรรมของลูกค้า และข้อผิดพลาดตุรกีไม่ได้มีความหมายอะไรกับฉันมากนัก

ดังนั้นเราจะบันทึกข้อความแสดงข้อผิดพลาดเป็นภาษาอังกฤษได้อย่างไรโดยไม่ต้องเปลี่ยนวัฒนธรรมผู้ใช้


8
ทำไมคุณถึงไม่สามารถเป็นแบบนี้ได้: CultureInfo oldCulture = Thread.CurrentThread.CurrentCulture; Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture ("en"); // โยนข้อยกเว้นใหม่ที่นี่ => วัฒนธรรมเป็นภาษาอังกฤษ Thread.CurrentThread.CurrentCulture = oldCulture
CheGueVerra

93
ฉันรู้ว่าไม่มีนักพัฒนาที่มีความสุขสำหรับข้อความที่ไม่ใช่ภาษาอังกฤษ: S ..
Zéiksz

3
@ Zéikszมองข้ามประเทศที่พูดภาษาอังกฤษและคุณจะพบพวกเขามากมาย: D ปัญหาไม่ใช่ข้อความที่ไม่ใช่ภาษาอังกฤษปัญหาคือภาษาที่คุณไม่เข้าใจ ข้อความในภาษาพื้นเมืองของคุณ (สมมติว่ามีการแปลที่เหมาะสม) นั้นใช้ได้อย่างสมบูรณ์แบบ
Alejandro

31
@Alejandro ต้องแปลข้อความแสดงข้อยกเว้นจากภาษาท้องถิ่นหนึ่งเป็นภาษาอังกฤษเพื่อที่จะ google มันเป็นความเจ็บปวดที่ยิ่งใหญ่ในตูด IMHO
แอนทอนเมลทซ์เฮม

18
คนงี่เง่าที่ Microsoft คนใดมีความคิดที่จะแปลข้อความแสดงข้อผิดพลาดสำหรับนักพัฒนาเท่านั้น แม้แต่คำที่ใช้ในภาษาการเขียนโปรแกรมเช่นคีย์ในพจนานุกรมก็แปล (ไม่พบรหัสในพจนานุกรมกลายเป็น Sleutel ใน niet gevonden ใน de bibliotheek ในภาษาดัตช์) ฉันไม่ต้องการที่จะเปลี่ยนระบบปฏิบัติการภาษาสำหรับการนี้ ...
โรล

คำตอบ:


66

ปัญหานี้สามารถแก้ไขได้บางส่วน รหัสข้อผิดพลาดของ Framework โหลดข้อความแสดงข้อผิดพลาดจากทรัพยากรโดยขึ้นอยู่กับตำแหน่งที่ตั้งของเธรดปัจจุบัน ในกรณีที่มีข้อยกเว้นเกิดขึ้นในเวลาที่มีการเข้าถึงคุณสมบัติข้อความ

สำหรับข้อยกเว้นเหล่านั้นคุณสามารถรับข้อความเวอร์ชั่นภาษาอังกฤษแบบสหรัฐอเมริกาโดยการเปลี่ยนโลแคลของเธรดเป็น en-US ในขณะที่บันทึกไว้ (บันทึกตำแหน่งโลแคลดั้งเดิมของผู้ใช้ไว้ล่วงหน้าและกู้คืนในภายหลัง)

การทำเช่นนี้ในเธรดแยกต่างหากจะดียิ่งขึ้น: สิ่งนี้ทำให้มั่นใจได้ว่าจะไม่มีผลข้างเคียงใด ๆ ตัวอย่างเช่น:

try
{
  System.IO.StreamReader sr=new System.IO.StreamReader(@"c:\does-not-exist");
}
catch(Exception ex)
{
  Console.WriteLine(ex.ToString()); //Will display localized message
  ExceptionLogger el = new ExceptionLogger(ex);
  System.Threading.Thread t = new System.Threading.Thread(el.DoLog);
  t.CurrentUICulture = new System.Globalization.CultureInfo("en-US");
  t.Start();
}

ตำแหน่งที่ ExceptionLogger มีลักษณะเป็นอย่างไร:

class ExceptionLogger
{
  Exception _ex;

  public ExceptionLogger(Exception ex)
  {
    _ex = ex;
  }

  public void DoLog()
  {
    Console.WriteLine(_ex.ToString()); //Will display en-US message
  }
}

อย่างไรก็ตามในขณะที่Joeชี้ให้เห็นอย่างถูกต้องในความคิดเห็นเกี่ยวกับการแก้ไขก่อนหน้านี้ของคำตอบนี้ข้อความบางส่วนได้รับการโหลด (บางส่วน) แล้วจากทรัพยากรภาษาในเวลาที่มีข้อยกเว้นโยน

สิ่งนี้ใช้กับส่วน 'พารามิเตอร์ต้องไม่เป็นโมฆะ' ของข้อความที่สร้างขึ้นเมื่อมีข้อยกเว้นการโยนอาร์กิวเมนต์ ArgumentNullException ("foo") ตัวอย่างเช่น ในกรณีดังกล่าวข้อความจะยังคงปรากฏ (บางส่วน) เป็นภาษาท้องถิ่นแม้ว่าจะใช้รหัสข้างต้น

นอกเหนือจากการใช้แฮ็กที่ใช้งานไม่ได้เช่นการเรียกใช้โค้ดที่ไม่ใช่ UI ทั้งหมดของคุณบนเธรดที่มีโลแคลในสหรัฐอเมริกาเริ่มต้นดูเหมือนจะไม่มีอะไรที่คุณสามารถทำได้: รหัสข้อยกเว้น. NET Framework ไม่มี สิ่งอำนวยความสะดวกสำหรับการแทนที่ตำแหน่งที่ตั้งของข้อความแสดงข้อผิดพลาด


10
ตัวอย่างของคุณใช้งานได้กับ FileNotFoundException เนื่องจากทรัพยากรข้อความถูกเรียกคืนเมื่อมีการเข้าถึงคุณสมบัติข้อความไม่ใช่เมื่อมีการโยนข้อยกเว้น แต่สิ่งนี้ไม่เป็นความจริงสำหรับข้อยกเว้นทั้งหมด (เช่นลองโยน ArgumentNullException ใหม่ ("paramName"))
Joe

3
ฉันสับสน. ฉันได้ลองทำตามคำตอบของคุณแล้วและทดสอบมันฉันต้องการข้อยกเว้นของฉันเป็นภาษาฝรั่งเศสดังนั้นฉันจึงทำได้t.CurrentUICulture = new System.Globalization.CultureInfo("fr-FR");และt.CurrentCulture = new System.Globalization.CultureInfo("fr-FR");ยังมีข้อยกเว้นที่เกิดขึ้นเป็นภาษาอังกฤษ ...
VitalyB

7
@VitalyB ข้อความข้อยกเว้นที่แปลเป็นส่วนหนึ่งของชุดภาษา. NET Framework ดังนั้นหากคุณไม่ได้ติดตั้งชุดภาษาฝรั่งเศสคุณจะไม่ได้รับข้อความแปล
แดเนียลโรส

7
อย่างน้อยกับ. NET 4.5 ข้อยกเว้นทั้งหมดจะถูกยกตัวอย่างด้วยEnvironment.GetResourceString("...")ดังนั้นโซลูชันของคุณจะไม่ทำงานอีกต่อไป สิ่งที่ดีที่สุดคือการโยนข้อยกเว้นที่กำหนดเองกับข้อความ (ภาษาอังกฤษ) ของคุณเองและใช้คุณสมบัติ InnerException เพื่อเก็บเก่า
webber2k6

1
การสะท้อนเพื่อให้ได้ชื่อประเภทข้อยกเว้นอาจมีประโยชน์
Guillermo Prandi

67

คุณสามารถค้นหาข้อความข้อยกเว้นเดิมได้ที่unlocalize.com


5
พยายามค้นหาข้อความยกเว้นภาษาจีนบอกฉันNo records foundเสมอ
ไทเลอร์ลอง

1
ทางเลือกที่ไม่ดี เมื่อฉันส่งข้อยกเว้นของฉันไปยัง Google Analytics (หรือบริการคลาวด์อื่น ๆ ) ฉันจะมีกลุ่มข้อยกเว้นต่างกันสำหรับข้อยกเว้นเดียวกัน แต่เป็นภาษาที่แตกต่างกัน และฉันจะไม่สามารถเรียงลำดับตามการนับของแต่ละข้อยกเว้นเพราะมันไม่ได้สะท้อนจำนวนจริง (100 ในภาษาอังกฤษ, 77 ในภาษาจีน, 80 ในภาษาเกาหลี ... ฯลฯ )
Artemious

ฉันจำได้ว่าพบเว็บไซต์ที่สวยงามนี้หลายครั้งเมื่อฉันทิ้งข้อความข้อยกเว้นที่แปลเป็นภาษาท้องถิ่นลงใน Google ตอนนี้มันไม่สามารถใช้งานได้อีกต่อไป
Martin Braun

40

จุดที่ถกเถียงกันบางที แต่แทนที่จะตั้งค่าวัฒนธรรมเพื่อคุณสามารถตั้งค่าให้en-US InvariantในInvariantวัฒนธรรมข้อความแสดงข้อผิดพลาดเป็นภาษาอังกฤษ

Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;

มันมีข้อได้เปรียบในการไม่ลำเอียงโดยเฉพาะอย่างยิ่งสถานที่ที่พูดภาษาอังกฤษไม่ใช่ชาวอเมริกัน (aka หลีกเลี่ยงคำพูดเยาะเย้ยจากเพื่อนร่วมงาน)


1
เราควรเขียนบรรทัดเหล่านี้ในโครงการ ASP.NET ของเราที่ไหน? ขอบคุณ
jason

2
ฉันจะแนะนำที่ด้านบนใน Application_Start นั่นจะทำให้โครงการทั้งหมดดำเนินการเป็นภาษาอังกฤษ หากเป็นเพียงข้อความแสดงข้อผิดพลาดที่คุณต้องการคุณสามารถสร้างฟังก์ชั่นปกและเรียกมันในแต่ละcatchรายการ
MPelletier

5
สิ่งนี้จะไม่ทำให้ปุ่มกล่องข้อความมาตรฐานเป็นภาษาอังกฤษด้วยใช่ไหม นั่นอาจไม่เป็นพฤติกรรมที่ต้องการ
Nyerguds

12

นี่คือวิธีการแก้ปัญหาที่ไม่จำเป็นต้องมีการเข้ารหัสใด ๆ และทำงานได้แม้กระทั่งข้อความข้อยกเว้นที่โหลดเร็วเกินไปที่เราจะสามารถเปลี่ยนรหัสได้ (ตัวอย่างเช่นรหัส mscorlib)

อาจไม่สามารถใช้งานได้ในทุกกรณี (ขึ้นอยู่กับการตั้งค่าของคุณเนื่องจากคุณต้องสามารถสร้างไฟล์. config ไว้ที่ไฟล์. exe หลัก) แต่ใช้งานได้สำหรับฉัน ดังนั้นเพียงแค่สร้างapp.configใน dev (หรือ a [myapp].exe.configหรือweb.configในการผลิต) ที่มีบรรทัดดังต่อไปนี้:

<configuration>
  ...
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="mscorlib.resources" publicKeyToken="b77a5c561934e089"
                          culture="fr" /> <!-- change this to your language -->

        <bindingRedirect oldVersion="1.0.0.0-999.0.0.0" newVersion="999.0.0.0"/>
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Xml.resources" publicKeyToken="b77a5c561934e089"
                          culture="fr" /> <!-- change this to your language -->

        <bindingRedirect oldVersion="1.0.0.0-999.0.0.0" newVersion="999.0.0.0"/>
      </dependentAssembly>

      <!-- add other assemblies and other languages here -->

    </assemblyBinding>
  </runtime>
  ...
</configuration>

สิ่งนี้บอกให้กรอบการทำงานเปลี่ยนเส้นทางการเชื่อมโยงแอสเซมบลีmscorlibของทรัพยากรและSystem.Xmlทรัพยากรของสำหรับรุ่นระหว่าง 1 ถึง 999 ในภาษาฝรั่งเศส (วัฒนธรรมถูกตั้งค่าเป็น " fr") ไปยังแอสเซมบลีที่ ... ไม่มีอยู่ รุ่น 999)

ดังนั้นเมื่อ CLR จะมองหาทรัพยากรภาษาฝรั่งเศสสำหรับชุดประกอบสองชุดนี้ (mscorlib และ System.xml) จะไม่พบชุดประกอบเหล่านั้นและกลับไปใช้ภาษาอังกฤษอย่างงดงาม ขึ้นอยู่กับบริบทและการทดสอบของคุณคุณอาจต้องการเพิ่มชุดประกอบอื่น ๆ ในการเปลี่ยนเส้นทางเหล่านี้ (ชุดประกอบที่มีทรัพยากรที่มีการแปล)

แน่นอนว่าฉันไม่คิดว่านี่จะได้รับการสนับสนุนโดย Microsoft ดังนั้นคุณต้องยอมรับความเสี่ยงเอง ในกรณีที่คุณตรวจพบปัญหาคุณสามารถลบการกำหนดค่านี้และตรวจสอบว่าไม่เกี่ยวข้อง


1
ทำงานเมื่อต้องการผลลัพธ์ภาษาอังกฤษจากเครื่องมือทดสอบ
smg

พยายามนี้ แต่มันไม่ได้ผลสำหรับฉัน มีไฟล์ทรัพยากรอื่น ๆ ใน. net หรือไม่ ฉันจะหาพวกมันได้ที่ไหน
BluE

1
ค้นหาใน c: \ Windows \ Microsoft.NET \ Framework \ v4.0.30319 ทุกภาษามีโฟลเดอร์ตัวอักษร 2 ตัว จำไว้ว่าให้แทนที่ "fr" ในคำตอบข้างต้นด้วยภาษาจริงที่ใช้งานอยู่ "no" สำหรับ Norwegian, "da" สำหรับ danish, "sv" สำหรับ swedish และอื่น ๆ
Wolf5

หากต้องการสร้างรายการทั้งหมดให้ดูที่โฟลเดอร์นั้น มันมีไฟล์ทรัพยากรประมาณ 120 ไฟล์ เพิ่มแต่ละรายการลงในการกำหนดค่า นี่น่าจะเป็นทางออกเดียวสำหรับ Windows 10 และใหม่กว่าในตอนนี้เนื่องจากไม่มีวิธีถอนการติดตั้งชุดภาษา. Net ในหน้าต่างที่ใหม่กว่าอีกต่อไป (ส่วนหนึ่งของระบบปฏิบัติการ) มันวางไว้ใน GAC ทันทีดังนั้นการลบโฟลเดอร์ภาษาเหล่านั้นจึงไม่ทำงาน
Wolf5

10

Windows ต้องมีภาษา UI ที่คุณต้องการใช้ติดตั้ง มันไม่มีทางที่จะรู้ได้อย่างน่าอัศจรรย์ว่าข้อความที่แปลคืออะไร

ในสุดยอด windows 7 ของสหรัฐอเมริกาพร้อมการติดตั้ง pt-PT รหัสต่อไปนี้:

Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("pt-PT");
string msg1 = new DirectoryNotFoundException().Message;

Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("en-US");
string msg2 = new FileNotFoundException().Message;

Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("fr-FR");
string msg3 = new FileNotFoundException().Message;

สร้างข้อความใน pt-PT, en-US และ en-US เนื่องจากไม่มีไฟล์วัฒนธรรมฝรั่งเศสติดตั้งจึงมีค่าเริ่มต้นเป็นภาษาเริ่มต้นของ windows (ติดตั้ง?)


นั่นคือการแก้ไขปัญหา UI โปแลนด์ในสถานการณ์ของฉันติดตั้งแพคเกจภาษา MUI ~ 260MB โดยใช้โปรแกรม Vistalizator
Krzysztof Szynter

5

ฉันรู้ว่านี่เป็นหัวข้อเก่า แต่ฉันคิดว่าโซลูชันของฉันอาจเกี่ยวข้องกับใครก็ตามที่สะดุดในการค้นหาเว็บ:

ในตัวบันทึกข้อยกเว้นคุณสามารถบันทึก ex.GetType.ToString ซึ่งจะบันทึกชื่อของคลาสยกเว้น ฉันคาดหวังว่าชื่อของคลาสควรจะเป็นอิสระจากภาษาและดังนั้นจึงมักจะแสดงเป็นภาษาอังกฤษ (เช่น "System.FileNotFoundException") แต่ในปัจจุบันฉันไม่สามารถเข้าถึงระบบภาษาต่างประเทศเพื่อทดสอบ ความคิด

หากคุณต้องการข้อความแสดงข้อผิดพลาดเช่นกันคุณสามารถสร้างพจนานุกรมของชื่อคลาสที่เป็นไปได้ทั้งหมดและข้อความเทียบเท่าในภาษาใดก็ได้ที่คุณต้องการ แต่สำหรับภาษาอังกฤษฉันคิดว่าชื่อคลาสนั้นเหมาะสมอย่างสมบูรณ์


5
ใช้งานไม่ได้ ผมได้โยนโดยInvalidOperationException System.Xml.XmlWellFormedWriterคุณลองเดาว่าเกิดข้อผิดพลาดใดโดยไม่อ่านข้อความ อาจเป็นพันสิ่งที่แตกต่างกัน
Nyerguds

4
CultureInfo oldCI = Thread.CurrentThread.CurrentCulture;

Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture ("en-US");
Thread.CurrentThread.CurrentUICulture=new CultureInfo("en-US");
try
{
  System.IO.StreamReader sr=new System.IO.StreamReader(@"c:\does-not-exist");
}
catch(Exception ex)
{
  Console.WriteLine(ex.ToString())
}
Thread.CurrentThread.CurrentCulture = oldCI;
Thread.CurrentThread.CurrentUICulture = oldCI;

ไม่มี WORKAROUNDS

Tks :)


คุณลืม;
KansaiRobot

4

การตั้งค่าThread.CurrentThread.CurrentUICultureจะใช้ในการแปลข้อยกเว้น หากคุณต้องการข้อยกเว้นสองประเภท (สำหรับผู้ใช้หนึ่งข้อสำหรับคุณ) คุณสามารถใช้ฟังก์ชั่นต่อไปนี้เพื่อแปลข้อความแสดงข้อยกเว้น กำลังค้นหาทรัพยากร. NET-Libraries สำหรับข้อความต้นฉบับเพื่อรับรหัสทรัพยากรจากนั้นส่งคืนค่าที่แปลแล้ว แต่มีจุดอ่อนข้อหนึ่งที่ฉันยังไม่พบทางออกที่ดี: ข้อความที่มี {0} ในทรัพยากรจะไม่ถูกค้นพบ หากใครมีทางออกที่ดีฉันจะขอบคุณ

public static string TranslateExceptionMessage(Exception ex, CultureInfo targetCulture)
{
    try
    {
        Assembly assembly = ex.GetType().Assembly;
        ResourceManager resourceManager = new ResourceManager(assembly.GetName().Name, assembly);
        ResourceSet originalResources = resourceManager.GetResourceSet(Thread.CurrentThread.CurrentUICulture, createIfNotExists: true, tryParents: true);
        ResourceSet targetResources = resourceManager.GetResourceSet(targetCulture, createIfNotExists: true, tryParents: true);
        foreach (DictionaryEntry originalResource in originalResources)
            if (originalResource.Value.ToString().Equals(ex.Message.ToString(), StringComparison.Ordinal))
                return targetResources.GetString(originalResource.Key.ToString(), ignoreCase: false); // success

    }
    catch { }
    return ex.Message; // failed (error or cause it's not smart enough to find texts with '{0}'-patterns)
}

นั่นจะไม่ทำงานหากข้อยกเว้นมีพารามิเตอร์ที่จัดรูปแบบ
Nick Berardi

ใช่อย่างที่ฉันพูดว่า: "แต่มีจุดอ่อนหนึ่งที่ฉันยังไม่พบทางออกที่ดี: ข้อความที่มี {0} ในทรัพยากรจะไม่พบถ้ามีใครมีทางออกที่ดีฉันจะขอบคุณ"
Vortex852456

3

กรอบงาน. NET มาในสองส่วน:

  1. . NET Framework นั้นเอง
  2. ชุดภาษาของ. NET Framework

ข้อความทั้งหมด (เช่นข้อความข้อยกเว้นป้ายชื่อปุ่มบน MessageBox เป็นต้น) เป็นภาษาอังกฤษใน. NET Framework ชุดภาษามีข้อความภาษาท้องถิ่น

ขึ้นอยู่กับสถานการณ์ที่แน่นอนของคุณทางออกคือการถอนการติดตั้งชุดภาษา (เช่นบอกให้ลูกค้าทำเช่นนั้น) ในกรณีนั้นข้อความยกเว้นจะเป็นภาษาอังกฤษ อย่างไรก็ตามโปรดทราบว่าข้อความที่ระบุในกรอบงานอื่น ๆ ทั้งหมดจะเป็นภาษาอังกฤษเช่นกัน (เช่นป้ายชื่อปุ่มบน MessageBox แป้นพิมพ์ลัดสำหรับ ApplicationCommands)


ขอบคุณ !! ฉันพบว่ามันน่าขันที่กล่องโต้ตอบถอนการติดตั้งเป็นภาษาของชุดถอนการติดตั้งไม่ใช่ภาษาท้องถิ่น หมายเหตุด้านข้าง: ชุดภาษาดูเหมือนจะกลับมาทุกสองสามเดือน ฉันยังไม่ได้คิดออกว่าทำไม แต่ฉันคาดเดาอัปเดต / อัปเกรด
Choco Smith

@ChocoSmith ทุกครั้งที่มีการอัพเดต. NET Framework ผ่าน Windows Update ชุดภาษาจะถูกติดตั้งอีกครั้ง
แดเนียลโรส

5
การขอให้ลูกค้าถอนการติดตั้งชุดภาษาสำหรับภาษาของตัวเองไม่ใช่โซลูชันที่ทำงานได้
Nyerguds

2

ฉันนึกภาพหนึ่งในวิธีต่อไปนี้:

  1. คุณได้อ่านข้อยกเว้นเพียงข้อเดียวนั่นคือไม่ใช่คุณลักษณะของไคลเอ็นต์ดังนั้นคุณสามารถใช้สตริงที่ไม่ใช่ภาษาท้องถิ่นซึ่งจะไม่เปลี่ยนแปลงเมื่อคุณเรียกใช้ในโหมดตุรกี

  2. รวมรหัสข้อผิดพลาดเช่น0x00000001กับข้อผิดพลาดแต่ละข้อเพื่อให้คุณสามารถค้นหาได้อย่างง่ายดายในตารางภาษาอังกฤษ


9
ที่จะไม่ช่วยมากเมื่อพวกเขามีข้อยกเว้นโยนโดยส่วนประกอบภายในของกรอบสุทธิ . ปัญหาทั้งหมดนี้ใช้ไม่ได้กับข้อยกเว้นที่คุณใช้ เห็นได้ชัด Chooses โปรแกรมเมอร์ว่าข้อความที่จะรวมกับผู้ที่
Nyerguds

1

อ้างอิงจากคำตอบ Undercover1989 แต่จะคำนึงถึงพารามิเตอร์บัญชีและเมื่อข้อความประกอบด้วยสตริงทรัพยากรหลายรายการ (เช่นข้อยกเว้นอาร์กิวเมนต์)

public static string TranslateExceptionMessage(Exception exception, CultureInfo targetCulture)
{
    Assembly a = exception.GetType().Assembly;
    ResourceManager rm = new ResourceManager(a.GetName().Name, a);
    ResourceSet rsOriginal = rm.GetResourceSet(Thread.CurrentThread.CurrentUICulture, true, true);
    ResourceSet rsTranslated = rm.GetResourceSet(targetCulture, true, true);

    var result = exception.Message;

    foreach (DictionaryEntry item in rsOriginal)
    {
        if (!(item.Value is string message))
            continue;

        string translated = rsTranslated.GetString(item.Key.ToString(), false);

        if (!message.Contains("{"))
        {
            result = result.Replace(message, translated);
        }
        else
        {
            var pattern = $"{Regex.Escape(message)}";
            pattern = Regex.Replace(pattern, @"\\{([0-9]+)\}", "(?<group$1>.*)");

            var regex = new Regex(pattern);

            var replacePattern = translated;
            replacePattern = Regex.Replace(replacePattern, @"{([0-9]+)}", @"${group$1}");
            replacePattern = replacePattern.Replace("\\$", "$");

            result = regex.Replace(result, replacePattern);
        }
    }

    return result;
}

1

ฉันมีสถานการณ์เดียวกันและคำตอบทั้งหมดที่ฉันพบที่นี่และที่อื่นไม่ได้ช่วยหรือไม่พอใจ:

การThread.CurrentUICultureเปลี่ยนแปลงภาษาของข้อยกเว้น. net แต่ไม่ได้Win32Exceptionใช้ซึ่งใช้ทรัพยากร Windows ในภาษาของ Windows UI เอง ดังนั้นฉันไม่เคยจัดการพิมพ์ข้อความเป็นWin32Exceptionภาษาอังกฤษแทนที่จะเป็นภาษาเยอรมันไม่ใช่แม้แต่ใช้FormatMessage()ตามที่อธิบายไว้ใน
วิธีรับ Win32Exception เป็นภาษาอังกฤษ

ดังนั้นฉันจึงสร้างโซลูชันของตัวเองซึ่งเก็บข้อความยกเว้นส่วนใหญ่สำหรับภาษาต่าง ๆ ไว้ในไฟล์ภายนอก คุณจะไม่ได้รับข้อความที่แน่นอนในภาษาที่คุณต้องการ แต่คุณจะได้รับข้อความในภาษานั้นซึ่งมากกว่าที่คุณได้รับในขณะนี้ (ซึ่งเป็นข้อความในภาษาที่คุณไม่เข้าใจ)

ฟังก์ชั่นคงที่ของคลาสนี้สามารถดำเนินการในการติดตั้ง Windows ด้วยภาษาที่ต่างกัน: CreateMessages()สร้างข้อความเฉพาะวัฒนธรรม
SaveMessagesToXML()บันทึกไว้เป็นไฟล์ XML ได้มากเท่าที่ภาษาถูกสร้างขึ้นหรือ
LoadMessagesFromXML()โหลดไฟล์ XML ทั้งหมดพร้อมข้อความเฉพาะภาษา

เมื่อสร้างไฟล์ XML ในการติดตั้ง Windows ที่มีภาษาต่างกันคุณจะมีทุกภาษาที่คุณต้องการ
บางทีคุณอาจสร้างข้อความสำหรับภาษาต่าง ๆ ใน 1 Windows เมื่อคุณติดตั้งชุดภาษา MUI หลายชุด แต่ฉันยังไม่ได้ทดสอบ

ทดสอบกับ VS2008 พร้อมใช้งาน ความคิดเห็นและข้อเสนอแนะยินดีต้อนรับ!

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Reflection;
using System.Threading;
using System.Xml;

public struct CException
{
  //----------------------------------------------------------------------------
  public CException(Exception i_oException)
  {
    m_oException = i_oException;
    m_oCultureInfo = null;
    m_sMessage = null;
  }

  //----------------------------------------------------------------------------
  public CException(Exception i_oException, string i_sCulture)
  {
    m_oException = i_oException;
    try
    { m_oCultureInfo = new CultureInfo(i_sCulture); }
    catch
    { m_oCultureInfo = CultureInfo.InvariantCulture; }
    m_sMessage = null;
  }

  //----------------------------------------------------------------------------
  public CException(Exception i_oException, CultureInfo i_oCultureInfo)
  {
    m_oException = i_oException;
    m_oCultureInfo = i_oCultureInfo == null ? CultureInfo.InvariantCulture : i_oCultureInfo;
    m_sMessage = null;
  }

  //----------------------------------------------------------------------------
  // GetMessage
  //----------------------------------------------------------------------------
  public string GetMessage() { return GetMessage(m_oException, m_oCultureInfo); }

  public string GetMessage(String i_sCulture) { return GetMessage(m_oException, i_sCulture); }

  public string GetMessage(CultureInfo i_oCultureInfo) { return GetMessage(m_oException, i_oCultureInfo); }

  public static string GetMessage(Exception i_oException) { return GetMessage(i_oException, CultureInfo.InvariantCulture); }

  public static string GetMessage(Exception i_oException, string i_sCulture)
  {
    CultureInfo oCultureInfo = null;
    try
    { oCultureInfo = new CultureInfo(i_sCulture); }
    catch
    { oCultureInfo = CultureInfo.InvariantCulture; }
    return GetMessage(i_oException, oCultureInfo);
  }

  public static string GetMessage(Exception i_oException, CultureInfo i_oCultureInfo)
  {
    if (i_oException == null) return null;
    if (i_oCultureInfo == null) i_oCultureInfo = CultureInfo.InvariantCulture;

    if (ms_dictCultureExceptionMessages == null) return null;
    if (!ms_dictCultureExceptionMessages.ContainsKey(i_oCultureInfo))
      return CreateMessage(i_oException, i_oCultureInfo);

    Dictionary<string, string> dictExceptionMessage = ms_dictCultureExceptionMessages[i_oCultureInfo];
    string sExceptionName = i_oException.GetType().FullName;
    sExceptionName = MakeXMLCompliant(sExceptionName);
    Win32Exception oWin32Exception = (Win32Exception)i_oException;
    if (oWin32Exception != null)
      sExceptionName += "_" + oWin32Exception.NativeErrorCode;
    if (dictExceptionMessage.ContainsKey(sExceptionName))
      return dictExceptionMessage[sExceptionName];
    else
      return CreateMessage(i_oException, i_oCultureInfo);
  }

  //----------------------------------------------------------------------------
  // CreateMessages
  //----------------------------------------------------------------------------
  public static void CreateMessages(CultureInfo i_oCultureInfo)
  {
    Thread oTH = new Thread(new ThreadStart(CreateMessagesInThread));
    if (i_oCultureInfo != null)
    {
      oTH.CurrentCulture = i_oCultureInfo;
      oTH.CurrentUICulture = i_oCultureInfo;
    }
    oTH.Start();
    while (oTH.IsAlive)
    { Thread.Sleep(10); }
  }

  //----------------------------------------------------------------------------
  // LoadMessagesFromXML
  //----------------------------------------------------------------------------
  public static void LoadMessagesFromXML(string i_sPath, string i_sBaseFilename)
  {
    if (i_sBaseFilename == null) i_sBaseFilename = msc_sBaseFilename;

    string[] asFiles = null;
    try
    {
      asFiles = System.IO.Directory.GetFiles(i_sPath, i_sBaseFilename + "_*.xml");
    }
    catch { return; }

    ms_dictCultureExceptionMessages.Clear();
    for (int ixFile = 0; ixFile < asFiles.Length; ixFile++)
    {
      string sXmlPathFilename = asFiles[ixFile];

      XmlDocument xmldoc = new XmlDocument();
      try
      {
        xmldoc.Load(sXmlPathFilename);
        XmlNode xmlnodeRoot = xmldoc.SelectSingleNode("/" + msc_sXmlGroup_Root);

        string sCulture = xmlnodeRoot.SelectSingleNode(msc_sXmlGroup_Info + "/" + msc_sXmlData_Culture).Value;
        CultureInfo oCultureInfo = new CultureInfo(sCulture);

        XmlNode xmlnodeMessages = xmlnodeRoot.SelectSingleNode(msc_sXmlGroup_Messages);
        XmlNodeList xmlnodelistMessage = xmlnodeMessages.ChildNodes;
        Dictionary<string, string> dictExceptionMessage = new Dictionary<string, string>(xmlnodelistMessage.Count + 10);
        for (int ixNode = 0; ixNode < xmlnodelistMessage.Count; ixNode++)
          dictExceptionMessage.Add(xmlnodelistMessage[ixNode].Name, xmlnodelistMessage[ixNode].InnerText);
        ms_dictCultureExceptionMessages.Add(oCultureInfo, dictExceptionMessage);
      }
      catch
      { return; }
    }
  }

  //----------------------------------------------------------------------------
  // SaveMessagesToXML
  //----------------------------------------------------------------------------
  public static void SaveMessagesToXML(string i_sPath, string i_sBaseFilename)
  {
    if (i_sBaseFilename == null) i_sBaseFilename = msc_sBaseFilename;

    foreach (KeyValuePair<CultureInfo, Dictionary<string, string>> kvpCultureExceptionMessages in ms_dictCultureExceptionMessages)
    {
      string sXmlPathFilename = i_sPath + i_sBaseFilename + "_" + kvpCultureExceptionMessages.Key.TwoLetterISOLanguageName + ".xml";
      Dictionary<string, string> dictExceptionMessage = kvpCultureExceptionMessages.Value;

      XmlDocument xmldoc = new XmlDocument();
      XmlWriter xmlwriter = null;
      XmlWriterSettings writerSettings = new XmlWriterSettings();
      writerSettings.Indent = true;

      try
      {
        XmlNode xmlnodeRoot = xmldoc.CreateElement(msc_sXmlGroup_Root);
        xmldoc.AppendChild(xmlnodeRoot);
        XmlNode xmlnodeInfo = xmldoc.CreateElement(msc_sXmlGroup_Info);
        XmlNode xmlnodeMessages = xmldoc.CreateElement(msc_sXmlGroup_Messages);
        xmlnodeRoot.AppendChild(xmlnodeInfo);
        xmlnodeRoot.AppendChild(xmlnodeMessages);

        XmlNode xmlnodeCulture = xmldoc.CreateElement(msc_sXmlData_Culture);
        xmlnodeCulture.InnerText = kvpCultureExceptionMessages.Key.Name;
        xmlnodeInfo.AppendChild(xmlnodeCulture);

        foreach (KeyValuePair<string, string> kvpExceptionMessage in dictExceptionMessage)
        {
          XmlNode xmlnodeMsg = xmldoc.CreateElement(kvpExceptionMessage.Key);
          xmlnodeMsg.InnerText = kvpExceptionMessage.Value;
          xmlnodeMessages.AppendChild(xmlnodeMsg);
        }

        xmlwriter = XmlWriter.Create(sXmlPathFilename, writerSettings);
        xmldoc.WriteTo(xmlwriter);
      }
      catch (Exception e)
      { return; }
      finally
      { if (xmlwriter != null) xmlwriter.Close(); }
    }
  }

  //----------------------------------------------------------------------------
  // CreateMessagesInThread
  //----------------------------------------------------------------------------
  private static void CreateMessagesInThread()
  {
    Thread.CurrentThread.Name = "CException.CreateMessagesInThread";

    Dictionary<string, string> dictExceptionMessage = new Dictionary<string, string>(0x1000);

    GetExceptionMessages(dictExceptionMessage);
    GetExceptionMessagesWin32(dictExceptionMessage);

    ms_dictCultureExceptionMessages.Add(Thread.CurrentThread.CurrentUICulture, dictExceptionMessage);
  }

  //----------------------------------------------------------------------------
  // GetExceptionTypes
  //----------------------------------------------------------------------------
  private static List<Type> GetExceptionTypes()
  {
    Assembly[] aoAssembly = AppDomain.CurrentDomain.GetAssemblies();

    List<Type> listoExceptionType = new List<Type>();

    Type oExceptionType = typeof(Exception);
    for (int ixAssm = 0; ixAssm < aoAssembly.Length; ixAssm++)
    {
      if (!aoAssembly[ixAssm].GlobalAssemblyCache) continue;
      Type[] aoType = aoAssembly[ixAssm].GetTypes();
      for (int ixType = 0; ixType < aoType.Length; ixType++)
      {
        if (aoType[ixType].IsSubclassOf(oExceptionType))
          listoExceptionType.Add(aoType[ixType]);
      }
    }

    return listoExceptionType;
  }

  //----------------------------------------------------------------------------
  // GetExceptionMessages
  //----------------------------------------------------------------------------
  private static void GetExceptionMessages(Dictionary<string, string> i_dictExceptionMessage)
  {
    List<Type> listoExceptionType = GetExceptionTypes();
    for (int ixException = 0; ixException < listoExceptionType.Count; ixException++)
    {
      Type oExceptionType = listoExceptionType[ixException];
      string sExceptionName = MakeXMLCompliant(oExceptionType.FullName);
      try
      {
        if (i_dictExceptionMessage.ContainsKey(sExceptionName))
          continue;
        Exception e = (Exception)(Activator.CreateInstance(oExceptionType));
        i_dictExceptionMessage.Add(sExceptionName, e.Message);
      }
      catch (Exception)
      { i_dictExceptionMessage.Add(sExceptionName, null); }
    }
  }

  //----------------------------------------------------------------------------
  // GetExceptionMessagesWin32
  //----------------------------------------------------------------------------
  private static void GetExceptionMessagesWin32(Dictionary<string, string> i_dictExceptionMessage)
  {
    string sTypeName = MakeXMLCompliant(typeof(Win32Exception).FullName) + "_";
    for (int iError = 0; iError < 0x4000; iError++)  // Win32 errors may range from 0 to 0xFFFF
    {
      Exception e = new Win32Exception(iError);
      if (!e.Message.StartsWith("Unknown error (", StringComparison.OrdinalIgnoreCase))
        i_dictExceptionMessage.Add(sTypeName + iError, e.Message);
    }
  }

  //----------------------------------------------------------------------------
  // CreateMessage
  //----------------------------------------------------------------------------
  private static string CreateMessage(Exception i_oException, CultureInfo i_oCultureInfo)
  {
    CException oEx = new CException(i_oException, i_oCultureInfo);
    Thread oTH = new Thread(new ParameterizedThreadStart(CreateMessageInThread));
    oTH.Start(oEx);
    while (oTH.IsAlive)
    { Thread.Sleep(10); }
    return oEx.m_sMessage;
  }

  //----------------------------------------------------------------------------
  // CreateMessageInThread
  //----------------------------------------------------------------------------
  private static void CreateMessageInThread(Object i_oData)
  {
    if (i_oData == null) return;
    CException oEx = (CException)i_oData;
    if (oEx.m_oException == null) return;

    Thread.CurrentThread.CurrentUICulture = oEx.m_oCultureInfo == null ? CultureInfo.InvariantCulture : oEx.m_oCultureInfo;
    // create new exception in desired culture
    Exception e = null;
    Win32Exception oWin32Exception = (Win32Exception)(oEx.m_oException);
    if (oWin32Exception != null)
      e = new Win32Exception(oWin32Exception.NativeErrorCode);
    else
    {
      try
      {
        e = (Exception)(Activator.CreateInstance(oEx.m_oException.GetType()));
      }
      catch { }
    }
    if (e != null)
      oEx.m_sMessage = e.Message;
  }

  //----------------------------------------------------------------------------
  // MakeXMLCompliant
  // from https://www.w3.org/TR/xml/
  //----------------------------------------------------------------------------
  private static string MakeXMLCompliant(string i_sName)
  {
    if (string.IsNullOrEmpty(i_sName))
      return "_";

    System.Text.StringBuilder oSB = new System.Text.StringBuilder();
    for (int ixChar = 0; ixChar < (i_sName == null ? 0 : i_sName.Length); ixChar++)
    {
      char character = i_sName[ixChar];
      if (IsXmlNodeNameCharacterValid(ixChar, character))
        oSB.Append(character);
    }
    if (oSB.Length <= 0)
      oSB.Append("_");
    return oSB.ToString();
  }

  //----------------------------------------------------------------------------
  private static bool IsXmlNodeNameCharacterValid(int i_ixPos, char i_character)
  {
    if (i_character == ':') return true;
    if (i_character == '_') return true;
    if (i_character >= 'A' && i_character <= 'Z') return true;
    if (i_character >= 'a' && i_character <= 'z') return true;
    if (i_character >= 0x00C0 && i_character <= 0x00D6) return true;
    if (i_character >= 0x00D8 && i_character <= 0x00F6) return true;
    if (i_character >= 0x00F8 && i_character <= 0x02FF) return true;
    if (i_character >= 0x0370 && i_character <= 0x037D) return true;
    if (i_character >= 0x037F && i_character <= 0x1FFF) return true;
    if (i_character >= 0x200C && i_character <= 0x200D) return true;
    if (i_character >= 0x2070 && i_character <= 0x218F) return true;
    if (i_character >= 0x2C00 && i_character <= 0x2FEF) return true;
    if (i_character >= 0x3001 && i_character <= 0xD7FF) return true;
    if (i_character >= 0xF900 && i_character <= 0xFDCF) return true;
    if (i_character >= 0xFDF0 && i_character <= 0xFFFD) return true;
    // if (i_character >= 0x10000 && i_character <= 0xEFFFF) return true;

    if (i_ixPos > 0)
    {
      if (i_character == '-') return true;
      if (i_character == '.') return true;
      if (i_character >= '0' && i_character <= '9') return true;
      if (i_character == 0xB7) return true;
      if (i_character >= 0x0300 && i_character <= 0x036F) return true;
      if (i_character >= 0x203F && i_character <= 0x2040) return true;
    }
    return false;
  }

  private static string msc_sBaseFilename = "exception_messages";
  private static string msc_sXmlGroup_Root = "exception_messages";
  private static string msc_sXmlGroup_Info = "info";
  private static string msc_sXmlGroup_Messages = "messages";
  private static string msc_sXmlData_Culture = "culture";

  private Exception m_oException;
  private CultureInfo m_oCultureInfo;
  private string m_sMessage;

  static Dictionary<CultureInfo, Dictionary<string, string>> ms_dictCultureExceptionMessages = new Dictionary<CultureInfo, Dictionary<string, string>>();
}

internal class Program
{
  public static void Main()
  {
    CException.CreateMessages(null);
    CException.SaveMessagesToXML(@"d:\temp\", "emsg");
    CException.LoadMessagesFromXML(@"d:\temp\", "emsg");
  }
}

1
Thread.CurrentUICulture ยังเปลี่ยนแปลงภาษาของ UI ที่ทำให้มันเป็นตัวเลือกที่น่ากลัว ตัวอย่างคลาสสิกคือปุ่มใช่ / ไม่ใช่ / ตกลง / ยกเลิกในกล่องข้อความ
Nyerguds

0

ข้อความยกเว้นเป็นภาษาอังกฤษ

try
{
    ......
}
catch (Exception ex)
{
      throw new UserFriendlyException(L("ExceptionmessagesinEnglish"));
}

จากนั้นไปที่โฟลเดอร์ Localization และวางไว้ใน projectName.xml และเพิ่ม

<text name="ExceptionmessagesinEnglish">Exception Message in English</text>

-1

คุณควรบันทึกการโทรสแต็คแทนข้อความแสดงข้อผิดพลาด (IIRC ข้อยกเว้นง่ายๆ ToString () ควรทำเพื่อคุณ) จากตรงนั้นคุณสามารถกำหนดได้ว่าข้อยกเว้นนั้นมาจากที่ใด


3
เรากำลังบันทึกข้อความและสแต็คเทรซ แต่จะง่ายกว่ามากหากข้อความนั้นชัดเจน
Carra

-1

แทนที่ข้อความข้อยกเว้นใน catch block โดยใช้วิธีการขยายตรวจสอบข้อความที่ส่งออกมาจากโค้ดหรือไม่ตามที่ระบุไว้ด้านล่าง

    public static string GetEnglishMessageAndStackTrace(this Exception ex)
    {
        CultureInfo currentCulture = Thread.CurrentThread.CurrentUICulture;
        try
        {

            dynamic exceptionInstanceLocal = System.Activator.CreateInstance(ex.GetType());
            string str;
            Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US");

            if (ex.Message == exceptionInstanceLocal.Message)
            {
                dynamic exceptionInstanceENG = System.Activator.CreateInstance(ex.GetType());

                str = exceptionInstanceENG.ToString() + ex.StackTrace;

            }
            else
            {
                str = ex.ToString();
            }
            Thread.CurrentThread.CurrentUICulture = currentCulture;

            return str;

        }
        catch (Exception)
        {
            Thread.CurrentThread.CurrentUICulture = currentCulture;

            return ex.ToString();
        }

1
ที่ผมกล่าวมาก่อน InvalidOperationException... ขอให้สนุกกับการหาว่ามันแปลว่าอะไรโดยไม่มีข้อความ อินสแตนซ์ใหม่จะไม่มีมันอย่างน่าอัศจรรย์
Nyerguds

-1

สำหรับวัตถุประสงค์ในการเข้าสู่ระบบแอปพลิเคชันบางอย่างอาจต้องดึงข้อความข้อยกเว้นภาษาอังกฤษ (นอกเหนือจากการแสดงใน UICulture ของลูกค้าปกติ)

เพื่อจุดประสงค์รหัสต่อไปนี้

  1. เปลี่ยน UICulture ปัจจุบัน
  2. สร้างวัตถุข้อยกเว้นที่ส่งออกมาอีกครั้งโดยใช้ "GetType ()" & "Activator.CreateInstance (t)"
  3. แสดงข้อความยกเว้นวัตถุใหม่ใน UICuture ใหม่
  4. จากนั้นในที่สุดก็เปลี่ยน UICulture ปัจจุบันกลับไปเป็น UICulture ก่อนหน้า

        try
        {
            int[] a = { 3, 6 };
            Console.WriteLine(a[3]); //Throws index out of bounds exception
    
            System.IO.StreamReader sr = new System.IO.StreamReader(@"c:\does-not-exist"); // throws file not found exception
            throw new System.IO.IOException();
    
        }
        catch (Exception ex)
        {
    
            Console.WriteLine(ex.Message);
            Type t = ex.GetType();
    
            CultureInfo CurrentUICulture = System.Threading.Thread.CurrentThread.CurrentUICulture;
    
            System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en-US");
    
            object o = Activator.CreateInstance(t);
    
            System.Threading.Thread.CurrentThread.CurrentUICulture = CurrentUICulture; // Changing the UICulture back to earlier culture
    
    
            Console.WriteLine(((Exception)o).Message.ToString());
            Console.ReadLine();
    
         }

1
สิ่งนี้ไม่รับประกันว่าข้อความแสดงข้อยกเว้นของวัตถุใหม่จะเหมือนกับข้อความยกเว้นที่ส่งออกมา มันอาจจะแตกต่างกันโดยสิ้นเชิงและมักจะแตกต่างกันโดยสิ้นเชิง นั่นเป็นเหตุผลที่เราต้องการข้อความแสดงข้อยกเว้น
Artemious
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.