แนวทางปฏิบัติที่ดีที่สุดโดยใช้ NSLocalizedString


140

ฉัน (เหมือนคนอื่น ๆ ทั้งหมด) ใช้NSLocalizedStringเพื่อแปลแอปของฉัน

น่าเสียดายที่มี "ข้อเสีย" หลายประการ (ไม่จำเป็นต้องเป็นความผิดของ NSLocalizedString เอง) รวมถึง

  • ไม่มีการเติมข้อความอัตโนมัติสำหรับสตริงใน Xcode สิ่งนี้ทำให้การทำงานไม่เพียง แต่เกิดข้อผิดพลาดได้ง่าย แต่ยังน่าเบื่อ
  • คุณอาจสิ้นสุดการกำหนดสตริงใหม่เนื่องจากคุณไม่รู้ว่ามีสตริงเทียบเท่าอยู่แล้ว (เช่น "โปรดป้อนรหัสผ่าน" และ "ป้อนรหัสผ่านก่อน")
  • เช่นเดียวกับปัญหาการเติมข้อความอัตโนมัติคุณต้อง "จดจำ" / คัดลอกสตริงความคิดเห็นไม่เช่นgenstringนั้นจะมีหลายความคิดเห็นสำหรับหนึ่งสตริง
  • หากคุณต้องการใช้genstringหลังจากที่คุณแปลสตริงบางส่วนแล้วคุณต้องระวังไม่ให้สูญเสียการแปลท้องถิ่นของคุณ
  • สายเดียวกันกระจัดกระจายตลอดโครงการของคุณทั้งหมด ตัวอย่างเช่นคุณใช้NSLocalizedString(@"Abort", @"Cancel action")ทุกที่จากนั้นตรวจสอบรหัสขอให้คุณเปลี่ยนชื่อสตริงNSLocalizedString(@"Cancel", @"Cancel action")เพื่อให้รหัสสอดคล้องกันมากขึ้น

สิ่งที่ฉันทำ (และหลังจากการค้นหาบางอย่างดังนั้นฉันคิดว่าหลายคนทำสิ่งนี้) คือการมีstrings.hไฟล์แยกต่างหากที่ฉัน#defineทุกรหัสภาษา ตัวอย่างเช่น

// In strings.h
#define NSLS_COMMON_CANCEL NSLocalizedString(@"Cancel", nil)
// Somewhere else
NSLog(@"%@", NSLS_COMMON_CANCEL);

สิ่งนี้จะช่วยให้การสร้างโค้ดเสร็จสมบูรณ์เป็นที่เดียวในการเปลี่ยนชื่อตัวแปร (ดังนั้นจึงไม่จำเป็นต้องมีจีสตริงอีกต่อไป) และคำสำคัญที่ไม่ซ้ำใครในการ refactor อัตโนมัติ อย่างไรก็ตามสิ่งนี้มาพร้อมกับค่าใช้จ่ายในการปิดท้ายด้วย#defineงบจำนวนมากที่ไม่ได้มีการจัดโครงสร้างอย่างแท้จริง (เช่น LocString.Common.Cancel หรืออะไรทำนองนั้น)

ดังนั้นในขณะที่วิธีนี้ใช้งานได้ดี แต่ฉันก็สงสัยว่าพวกคุณทำได้อย่างไรในโครงการของคุณ มีวิธีอื่นที่จะทำให้การใช้ NSLocalizedString ง่ายขึ้นหรือไม่? อาจมีกรอบที่ห่อหุ้มหรือไม่


ฉันแค่ทำมันเกือบจะเหมือนคุณ แต่ฉันกำลังใช้ makro NSLocalizedStringWithDefaultValue เพื่อสร้างไฟล์สตริงที่แตกต่างกันสำหรับปัญหาการแปลที่แตกต่างกัน (เช่นตัวควบคุมรุ่นและอื่น ๆ ) และเพื่อสร้างค่าเริ่มต้นเริ่มต้น
anka

ดูเหมือนว่าการส่งออกไปยังการแปลเป็นภาษาท้องถิ่นของ xcode6 ไม่ได้จับสตริงที่กำหนดเป็นมาโครในไฟล์ส่วนหัว ทุกคนสามารถยืนยันหรือบอกสิ่งที่ฉันอาจจะหายไป? ขอบคุณ ... !
Juddster

@Juddster สามารถยืนยันแม้จะมีตัวแก้ไขกองทุนใหม่ -> ส่งออกสำหรับการแปลไม่ได้รับในไฟล์ส่วนหัว
Red

คำตอบ:


100

NSLocalizedStringมีข้อ จำกัด เล็กน้อย แต่เป็นศูนย์กลางของ Cocoa ที่ไม่มีเหตุผลในการเขียนรหัสที่กำหนดเองเพื่อจัดการการแปลซึ่งหมายความว่าคุณจะต้องใช้มัน ที่กล่าวว่าการใช้เครื่องมือเล็กน้อยสามารถช่วยได้นี่คือวิธีที่ฉันดำเนินการ:

การอัพเดตไฟล์ strings

genstringsเขียนทับไฟล์สตริงของคุณยกเลิกการแปลก่อนหน้าทั้งหมดของคุณ ฉันเขียนupdate_strings.pyเพื่อแยกไฟล์สายอักขระเก่ารันgenstringsและเติมในช่องว่างเพื่อที่คุณจะได้ไม่ต้องเรียกคืนการแปลที่มีอยู่ด้วยตนเอง สคริปต์พยายามจับคู่ไฟล์สตริงที่มีอยู่ให้ใกล้เคียงที่สุดเพื่อหลีกเลี่ยงความแตกต่างที่ใหญ่เกินไปเมื่อทำการอัพเดต

การตั้งชื่อสตริงของคุณ

หากคุณใช้NSLocalizedStringตามที่โฆษณาไว้:

NSLocalizedString(@"Cancel or continue?", @"Cancel notice message when a download takes too long to proceed");

คุณอาจกำหนดสตริงเดียวกันในส่วนอื่นของรหัสของคุณซึ่งอาจขัดแย้งกันเนื่องจากคำศัพท์ภาษาอังกฤษเดียวกันอาจมีความหมายแตกต่างกันในบริบทที่แตกต่างกัน ( OKและCancelนึกถึง) นั่นคือเหตุผลที่ฉันมักจะใช้สตริง all-caps ที่ไม่มีความหมายกับคำนำหน้าเฉพาะโมดูลและคำอธิบายที่แม่นยำมาก:

NSLocalizedString(@"DOWNLOAD_CANCEL_OR_CONTINUE", @"Cancel notice window title when a download takes too long to proceed");

ใช้สตริงเดียวกันในสถานที่ต่างกัน

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

ฉันหวังว่าคุณจะมีประสิทธิผลมากขึ้นด้วยการแปลภาษาโกโก้ด้วยเคล็ดลับเหล่านี้


ขอบคุณสำหรับคำตอบของคุณฉันจะดูไฟล์ไพ ธ อนของคุณอย่างแน่นอน ฉันเห็นด้วยกับแบบแผนการตั้งชื่อของคุณ ฉันได้พูดคุยกับ devs iOS อื่น ๆ เมื่อเร็ว ๆ นี้และพวกเขาแนะนำให้ใช้สตริงแบบคงที่แทนที่จะเป็นมาโครซึ่งเหมาะสมแล้ว ฉันตอบคำถามของคุณแล้ว แต่จะรอสักครู่ก่อนที่จะยอมรับเพราะวิธีการแก้ปัญหายังค่อนข้างงุ่มง่าม บางทีสิ่งที่ดีกว่ามาพร้อม ขอบคุณอีกครั้ง!
JiaYow

คุณยินดีอย่างมาก. การโลคัลไลซ์เซชันเป็นกระบวนการที่น่าเบื่อการมีเครื่องมือที่เหมาะสมและเวิร์กโฟลว์สร้างโลกที่แตกต่าง
ndfred

17
ฉันไม่เคยเข้าใจเลยว่าทำไมฟังก์ชั่นการโลคัลไลเซชันตามสไตล์ของ gettext จึงใช้หนึ่งในการแปลเป็นกุญแจ จะเกิดอะไรขึ้นถ้าข้อความต้นฉบับของคุณเปลี่ยนแปลง? การเปลี่ยนแปลงที่สำคัญของคุณและไฟล์ที่แปลทั้งหมดของคุณกำลังใช้ข้อความเก่าสำหรับรหัสของพวกเขา มันไม่สมเหตุสมผลสำหรับฉัน ฉันเคยใช้ปุ่มต่างๆเช่น "home_button_text" เพื่อให้มันไม่เหมือนใครและไม่เคยเปลี่ยน ฉันยังได้เขียนสคริปต์ทุบตีเพื่อแยกไฟล์ Localizable.strings ทั้งหมดของฉันและสร้างไฟล์คลาสด้วยวิธีสแตติกซึ่งจะโหลดสตริงที่เหมาะสม สิ่งนี้ทำให้ฉันโค้ดสำเร็จ อยู่มาวันหนึ่งฉันอาจเปิดแหล่งที่มานี้
Mike Weller

2
ฉันคิดว่าคุณหมายถึงไม่ได้genstrings gestring
ฮิโรชิ

1
@ndfred ตรวจสอบเวลาคอมไพล์ว่าคุณไม่ได้พิมพ์สตริงผิดนั่นคือการชนะที่ยิ่งใหญ่ที่สุด มันเป็นรหัสเพิ่มเติมเล็กน้อยเพื่อเพิ่มต่อไป นอกจากนี้ในกรณีของการเปลี่ยนโครงสร้างการวิเคราะห์แบบคงที่การมีสัญลักษณ์จะทำให้สิ่งต่าง ๆ ง่ายขึ้น
Allen Zeng

31

สำหรับ autocompletition สำหรับสตริงใน Xcode คุณสามารถลองhttps://github.com/questbeat/Lin


3
อันที่จริงมันน่าทึ่งมาก ไม่จำเป็นต้องสร้างมาโคร
Beau Nouvelle

1
ไม่พบหน้า _
Juanmi

1
@Juanmi ขอบคุณสำหรับการกล่าวถึงการเชื่อมโยงที่ตายแล้ว ฉันแทนที่ลิงค์ด้วย URL gitHub
ฮิโรชิ

24

เห็นด้วยกับ ndfred แต่ฉันต้องการเพิ่มสิ่งนี้:

พารามิเตอร์ที่สองสามารถใช้เป็น ... ค่าเริ่มต้น !!

(NSLocalizedStringWithDefaultValue ทำงานไม่ถูกต้องกับ genstring นั่นคือเหตุผลที่ฉันเสนอโซลูชันนี้)

นี่คือการใช้งานที่กำหนดเองของฉันที่ใช้ NSLocalizedString ที่ใช้ความคิดเห็นเป็นค่าเริ่มต้น:

1. ในส่วนหัวที่รวบรวมไว้ล่วงหน้า (ไฟล์. pch) ให้นิยามแมโคร 'NSLocalizedString' อีกครั้ง:

// cutom NSLocalizedString that use macro comment as default value
#import "LocalizationHandlerUtil.h"

#undef NSLocalizedString
#define NSLocalizedString(key,_comment) [[LocalizationHandlerUtil singleton] localizedString:key  comment:_comment]

2. สร้างคลาสเพื่อใช้งาน Localization Handler

#import "LocalizationHandlerUtil.h"

@implementation LocalizationHandlerUtil

static LocalizationHandlerUtil * singleton = nil;

+ (LocalizationHandlerUtil *)singleton
{
    return singleton;
}

__attribute__((constructor))
static void staticInit_singleton()
{
    singleton = [[LocalizationHandlerUtil alloc] init];
}

- (NSString *)localizedString:(NSString *)key comment:(NSString *)comment
{
    // default localized string loading
    NSString * localizedString = [[NSBundle mainBundle] localizedStringForKey:key value:key table:nil];

    // if (value == key) and comment is not nil -> returns comment
    if([localizedString isEqualToString:key] && comment !=nil)
        return comment;

    return localizedString;
}

@end

3. ใช้มัน!

ตรวจสอบให้แน่ใจว่าคุณเพิ่มสคริปต์เรียกใช้ใน App Build Phases ของคุณเพื่อให้ไฟล์ Localizable.strings ของคุณจะได้รับการอัปเดตในแต่ละบิลด์นั่นคือสตริงที่แปลเป็นภาษาท้องถิ่นใหม่จะถูกเพิ่มในไฟล์ Localized.strings ของคุณ:

Build phase Script ของฉันเป็นเชลล์สคริปต์:

Shell: /bin/sh
Shell script content: find . -name \*.m | xargs genstrings -o MyClassesFolder

ดังนั้นเมื่อคุณเพิ่มบรรทัดใหม่ในรหัสของคุณ:

self.title = NSLocalizedString(@"view_settings_title", @"Settings");

จากนั้นดำเนินการสร้างไฟล์. /Localizable.scripts ของคุณจะมีบรรทัดใหม่นี้:

/* Settings */
"view_settings_title" = "view_settings_title";

และเนื่องจากคีย์ == ค่าสำหรับ 'view_settings_title' LocalizedStringHandler ที่กำหนดเองจะส่งคืนความคิดเห็นเช่น 'การตั้งค่า "

Voilà :-)


รับข้อผิดพลาด ARC ไม่มีวิธีอินสแตนซ์ที่รู้จักสำหรับตัวเลือก 'localizedString: ความคิดเห็น: :(
Mangesh

ฉันคิดว่าเป็นเพราะ LocalizationHandlerUtil.h หายไป ฉันไม่พบรหัสกลับมา ... เพียงแค่พยายามสร้างไฟล์ส่วนหัว LocalizationHandlerUtil.h และควรจะตกลง
Pascal

ฉันสร้างไฟล์แล้ว ฉันคิดว่ามันเป็นเพราะปัญหาเส้นทางของโฟลเดอร์
Mangesh

3

ใน Swift ฉันใช้สิ่งต่อไปนี้เช่นสำหรับปุ่ม "ใช่" ในกรณีนี้:

NSLocalizedString("btn_yes", value: "Yes", comment: "Yes button")

หมายเหตุการใช้งานของvalue:สำหรับค่าข้อความเริ่มต้น พารามิเตอร์แรกทำหน้าที่เป็น ID การแปล ข้อดีของการใช้value:พารามิเตอร์คือข้อความเริ่มต้นสามารถเปลี่ยนแปลงได้ในภายหลัง แต่ ID การแปลยังคงเหมือนเดิม ไฟล์ Localizable.strings จะมี"btn_yes" = "Yes";

หากvalue:ไม่ได้ใช้พารามิเตอร์พารามิเตอร์แรกจะถูกใช้สำหรับทั้งสอง: สำหรับ ID การแปลและสำหรับค่าข้อความเริ่มต้น แฟ้ม Localizable.strings "Yes" = "Yes";จะมี การจัดการไฟล์การแปลภาษาแบบนี้ดูเหมือนจะแปลก โดยเฉพาะอย่างยิ่งถ้าข้อความที่แปลยาวเกินไป ID ก็ยาวเช่นกัน เมื่อใดก็ตามที่มีการเปลี่ยนแปลงอักขระของค่าข้อความเริ่มต้นรหัสการแปลก็จะเปลี่ยนเช่นกัน สิ่งนี้นำไปสู่ปัญหาเมื่อมีการใช้ระบบแปลภายนอก การเปลี่ยนรหัสแปลนั้นเป็นที่เข้าใจว่าเป็นการเพิ่มข้อความแปลใหม่ซึ่งอาจไม่ต้องการเสมอไป


2

ฉันเขียนสคริปต์เพื่อช่วยดูแล Localizable.strings ในหลายภาษา แม้ว่าจะไม่ได้ช่วยในการเติมข้อความอัตโนมัติ แต่ก็ช่วยในการผสานไฟล์. strings โดยใช้คำสั่ง:

merge_strings.rb ja.lproj/Localizable.strings en.lproj/Localizable.strings

สำหรับข้อมูลเพิ่มเติมโปรดดูที่: https://github.com/hiroshi/merge_strings

บางท่านคิดว่ามีประโยชน์ฉันหวังว่า


2

หากใครที่กำลังมองหาวิธีแก้ปัญหาอย่างรวดเร็ว คุณอาจต้องการตรวจสอบทางออกของฉันฉันใส่กันที่นี่: SwiftyLocalization

เพียงไม่กี่ขั้นตอนในการติดตั้งคุณจะได้รับการแปลอย่างยืดหยุ่นใน Google Spreadsheet (ความคิดเห็นสีที่กำหนดเองไฮไลต์แบบอักษรแผ่นงานหลายแผ่นและอื่น ๆ )

กล่าวโดยสรุปก็คือ: Google Spreadsheet -> ไฟล์ CSV -> Localizable.strings

ยิ่งไปกว่านั้นมันยังสร้าง Localizables.swift ซึ่งเป็นโครงสร้างที่ทำหน้าที่เหมือนอินเทอร์เฟซสำหรับการดึงและถอดรหัสกุญแจสำหรับคุณ (คุณต้องระบุวิธีถอดรหัสสตริงจากคีย์ด้วยตนเอง)

ทำไมถึงยิ่งใหญ่นี้

  1. คุณไม่จำเป็นต้องมีรหัสเป็นสตริงธรรมดาทั่วทุกสถานที่
  2. ตรวจพบคีย์ผิดในเวลารวบรวม
  3. Xcode สามารถเติมข้อความอัตโนมัติ

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

// It's defined as computed static var, so it's up-to-date every time you call. 
// You can also have your custom retrieval method there.

button.setTitle(Localizables.login.button_title_login, forState: .Normal)

โครงการใช้ Google App Script เพื่อแปลงแผ่นงาน -> CSV และสคริปต์ Python เพื่อแปลงไฟล์ CSV -> Localizable.strings คุณสามารถดูตัวอย่างแผ่นงานนี้ได้อย่างรวดเร็วเพื่อดูว่าอะไรเป็นไปได้


1

ด้วย iOS 7 และ Xcode 5 คุณควรหลีกเลี่ยงการใช้วิธีการ 'Localization.strings' และใช้วิธีการใหม่ 'การแปลพื้นฐาน' มีแบบฝึกหัดรอบ ๆ ถ้าคุณ google สำหรับ 'การแปลพื้นฐาน'

Apple doc: การแปลพื้นฐาน


ใช่สตีฟนั้นถูกต้อง นอกจากนี้คุณยังต้องใช้วิธีการไฟล์. strings สำหรับสตริงที่สร้างขึ้นแบบไดนามิก แต่สำหรับวิธีการเหล่านี้เท่านั้นแอปเปิ้ลที่ต้องการคือการแปลพื้นฐาน
Ronny Webers

เชื่อมโยงกับวิธีการใหม่หรือไม่
อธิที่

1
Imo วิธีการแปลพื้นฐานไม่มีค่า คุณยังต้องเก็บไฟล์ตำแหน่งอื่น ๆ สำหรับสตริงแบบไดนามิกและมันจะทำให้สตริงของคุณแพร่กระจายผ่านไฟล์จำนวนมาก สตริงภายใน Nibs / สตอรีบอร์ดสามารถแปลเป็นภาษาท้องถิ่นได้โดยอัตโนมัติไปยังคีย์ใน Localizable.strings กับ Libs บางอย่างเช่นgithub.com/AliSoftware/OHAutoNIBi18n
Rafael Nobre


0

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

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

NSLocalizedString\(@(".*?")\s*,\s*nil\) 

เพียงแทนที่ด้วยสิ่งที่ตรงกับมาโครของคุณและการใช้ NSLocalizedString

สคริปต์มาที่นี่คุณต้องการเพียงส่วนที่ 3 เท่านั้น ส่วนที่เหลือคือการดูง่ายขึ้นว่ามันมาจากไหน:

// Part 1. Get keys from one of the Localizable.strings
perl -ne 'print "$1\n" if /^\s*(".+")\s*=/' myapp/fr.lproj/Localizable.strings

// Part 2. Get keys from the source code
grep -n -h -Eo -r  'NSLocalizedString\(@(".*?")\s*,\s*nil\)' ./ | perl -ne 'print "$1\n" if /NSLocalizedString\(@(".+")\s*,\s*nil\)/'

// Part 3. Get Part 1 and 2 together.

comm -2 -3 <(grep -n -h -Eo -r  'NSLocalizedString\(@(".*?")\s*,\s*nil\)' ./ | perl -ne 'print "$1\n" if /NSLocalizedString\(@(".+")\s*,\s*nil\)/' | sort | uniq) <(perl -ne 'print "$1\n" if /^\s*(".+")\s*=/' myapp/fr.lproj/Localizable.strings | sort) | uniq >> fr-localization-delta.txt

ไฟล์เอาต์พุตมีคีย์ที่พบในรหัส แต่ไม่มีในไฟล์ Localizable.strings นี่คือตัวอย่าง:

"MPH"
"Map Direction"
"Max duration of a detailed recording, hours"
"Moving ..."
"My Track"
"New Trip"

แน่นอนสามารถขัดได้มากขึ้น แต่คิดว่าฉันจะแบ่งปัน

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