Praktik terbaik menggunakan NSLocalizedString

140

Saya (seperti yang lainnya) gunakan NSLocalizedStringuntuk melokalkan aplikasi saya.

Sayangnya, ada beberapa "kelemahan" (belum tentu kesalahan NSLocalizedString itu sendiri), termasuk

  • Tidak ada pelengkapan otomatis untuk string di Xcode. Ini membuat pekerjaan tidak hanya rawan kesalahan tetapi juga melelahkan.
  • Anda mungkin akhirnya mendefinisikan ulang string hanya karena Anda tidak tahu string yang setara sudah ada (yaitu "Silakan masukkan kata sandi" vs. "Masukkan kata sandi terlebih dahulu")
  • Mirip dengan masalah pelengkapan otomatis, Anda harus "mengingat" / menyalin string komentar, atau genstringakan berakhir dengan beberapa komentar untuk satu string
  • Jika Anda ingin menggunakan genstringsetelah Anda telah melokalisasi beberapa string, Anda harus berhati-hati untuk tidak kehilangan pelokalan lama Anda.
  • String yang sama tersebar di seluruh proyek Anda. Misalnya, Anda menggunakan di NSLocalizedString(@"Abort", @"Cancel action")mana-mana, dan kemudian Peninjauan Kode meminta Anda untuk mengganti nama string NSLocalizedString(@"Cancel", @"Cancel action")agar kode lebih konsisten.

Apa yang saya lakukan (dan setelah beberapa pencarian di SO saya pikir banyak orang melakukan ini) adalah memiliki strings.hfile terpisah di mana saya #definesemua kode-localize. Sebagai contoh

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

Ini pada dasarnya menyediakan penyelesaian kode, satu tempat untuk mengubah nama variabel (jadi tidak perlu lagi membuat genstring), dan kata kunci unik untuk auto-refactor. Namun, ini datang dengan biaya berakhir dengan sejumlah #definepernyataan yang tidak terstruktur secara inheren (yaitu seperti LocString.Common.Cancel atau sesuatu seperti itu).

Jadi, sementara ini bekerja agak baik, saya bertanya-tanya bagaimana kalian melakukannya dalam proyek Anda. Apakah ada pendekatan lain untuk menyederhanakan penggunaan NSLocalizedString? Apakah mungkin ada kerangka kerja yang merangkumnya?

JiaYow
sumber
Saya hanya melakukannya hampir sama seperti Anda. Tapi saya menggunakan NSLocalizedStringWithDefaultValue makro untuk membuat file string yang berbeda untuk masalah lokalisasi yang berbeda (seperti pengontrol, model, dll.) Dan untuk membuat nilai default awal.
anka
Sepertinya Ekspor xcode6 untuk pelokalan tidak menangkap string yang didefinisikan sebagai makro dalam file header. Adakah yang bisa mengkonfirmasi atau memberi tahu saya apa yang mungkin saya lewatkan? Terima kasih...!
Juddster
@Juddster, dapat mengkonfirmasi, bahkan dengan Editor dana baru-> Ekspor untuk Lokalisasi itu tidak dijemput dalam file header
Red

Jawaban:

100

NSLocalizedStringmemiliki beberapa batasan, tetapi sangat penting bagi Cocoa sehingga tidak masuk akal untuk menulis kode khusus untuk menangani pelokalan, yang berarti Anda harus menggunakannya. Yang mengatakan, sedikit perkakas dapat membantu, inilah cara saya melanjutkan:

Memperbarui file string

genstringsmenimpa file string Anda, membuang semua terjemahan Anda sebelumnya. Saya menulis update_strings.py untuk mem-parsing file string lama, jalankan genstringsdan isi yang kosong sehingga Anda tidak harus secara manual mengembalikan terjemahan yang ada. Script mencoba untuk mencocokkan file string yang ada sedekat mungkin untuk menghindari perbedaan yang terlalu besar saat memperbaruinya.

Memberi nama string Anda

Jika Anda menggunakan NSLocalizedStringseperti yang diiklankan:

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

Anda mungkin akhirnya mendefinisikan string yang sama di bagian lain dari kode Anda, yang mungkin bertentangan dengan istilah bahasa Inggris yang sama mungkin memiliki arti yang berbeda dalam konteks yang berbeda ( OKdan Cancelterlintas dalam pikiran). Itulah sebabnya saya selalu menggunakan string all-caps yang tidak berarti dengan awalan khusus modul, dan deskripsi yang sangat tepat:

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

Menggunakan string yang sama di tempat yang berbeda

Jika Anda menggunakan string yang sama beberapa kali, Anda bisa menggunakan makro seperti yang Anda lakukan, atau cache itu sebagai variabel instan di pengontrol tampilan Anda atau sumber data Anda. Dengan cara ini Anda tidak perlu mengulang deskripsi yang mungkin menjadi basi dan menjadi tidak konsisten di antara contoh lokalisasi yang sama, yang selalu membingungkan. Karena variabel instan adalah simbol, Anda akan dapat menggunakan pelengkapan otomatis pada terjemahan yang paling umum ini, dan menggunakan string "manual" untuk yang spesifik, yang hanya akan terjadi sekali saja.

Saya harap Anda akan lebih produktif dengan lokalisasi Kakao dengan tips ini!

ndfred
sumber
Terima kasih atas jawaban Anda, saya pasti akan melihat file python Anda. Saya setuju dengan konvensi penamaan Anda. Saya telah berbicara dengan beberapa pengembang iOS lainnya baru-baru ini, dan mereka merekomendasikan penggunaan string statis alih-alih makro, yang masuk akal. Saya telah membatalkan jawaban Anda, tetapi akan menunggu sedikit sebelum saya menerimanya, karena solusinya masih agak canggung. Mungkin sesuatu yang lebih baik datang. Terima kasih lagi!
JiaYow
Sama-sama. Lokalisasi adalah proses yang membosankan, memiliki alat dan alur kerja yang tepat membuat dunia berbeda.
ndfred
17
Saya tidak pernah mengerti mengapa fungsi lokalisasi gettext-style menggunakan salah satu terjemahan sebagai kuncinya. Apa yang terjadi jika teks asli Anda berubah? Kunci Anda berubah dan semua file lokal Anda menggunakan teks lama untuk kunci mereka. Tidak pernah masuk akal bagi saya. Saya selalu menggunakan kunci seperti "home_button_text" sehingga unik dan tidak pernah berubah. Saya juga telah menulis skrip bash untuk mem-parsing semua file Localizable.strings saya dan menghasilkan file kelas dengan metode statis yang akan memuat string yang sesuai. Ini memberi saya penyelesaian kode. Suatu hari saya mungkin membuka sumber ini.
Mike Weller
2
Saya pikir maksud Anda genstringstidak gestring.
hiroshi
1
@ndfred mengkompilasi waktu memeriksa bahwa Anda belum mengetik string yang salah adalah kemenangan terbesar. Ini sedikit lebih banyak kode untuk ditambahkan. Juga dalam kasus refactoring, analisis statis, memiliki simbol akan membuat segalanya lebih mudah.
Allen Zeng
31

Sedangkan untuk pelengkapan otomatis untuk string di Xcode, Anda dapat mencoba https://github.com/questbeat/Lin .

hiroshi
sumber
3
Ini sebenarnya sangat menakjubkan. Tidak perlu membuat makro.
Beau Nouvelle
1
Halaman tidak found_
juanmi
1
@Juanmi Terima kasih telah menyebutkan tautan yang mati. Saya mengganti tautan dengan url github.
hiroshi
24

Setuju dengan ndfred, tetapi saya ingin menambahkan ini:

Parameter kedua dapat digunakan sebagai ... nilai default !!

(NSLocalizedStringWithDefaultValue tidak bekerja dengan benar dengan genstring, itu sebabnya saya mengusulkan solusi ini)

Berikut ini adalah implementasi Kustom saya yang menggunakan NSLocalizedString yang menggunakan komentar sebagai nilai default:

1. Di tajuk yang dikompilasi sebelumnya (file .pch), tentukan ulang makro '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. membuat kelas untuk mengimplementasikan penangan lokalisasi

#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. Gunakan itu!

Pastikan Anda menambahkan skrip Run di App Build Phases Anda sehingga file Localizable.strings Anda akan diperbarui di setiap build, yaitu, string terlokalisasi baru akan ditambahkan dalam file Localized.strings Anda:

Skrip fase pembuatan saya adalah skrip shell:

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

Jadi, ketika Anda menambahkan baris baru ini dalam kode Anda:

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

Kemudian lakukan build, file ./Localizable.scripts Anda akan berisi baris baru ini:

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

Dan karena kunci == nilai untuk 'view_settings_title', custom LocalizedStringHandler akan mengembalikan komentar, yaitu 'Pengaturan "

Voa :-)

Pascal
sumber
Mendapatkan kesalahan ARC, Tidak ada metode contoh yang dikenal untuk pemilih 'localizedString: komentar: :(
Mangesh
Saya kira itu karena LocalizationHandlerUtil.h hilang. Saya tidak dapat menemukan kode kembali ... Hanya mencoba untuk membuat file header LocalizationHandlerUtil.h dan harus OK
Pascal
Saya telah membuat file. Saya pikir ini karena masalah jalur folder.
Mangesh
3

Di Swift saya menggunakan yang berikut ini, misalnya untuk tombol "Ya" dalam hal ini:

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

Perhatikan penggunaan value:untuk nilai teks default. Parameter pertama berfungsi sebagai ID terjemahan. Keuntungan menggunakan value:parameter adalah teks default dapat diubah kemudian tetapi ID terjemahannya tetap sama. File Localizable.strings akan berisi"btn_yes" = "Yes";

Jika value:parameter tidak digunakan maka parameter pertama akan digunakan untuk keduanya: untuk ID terjemahan dan juga untuk nilai teks default. File Localizable.strings akan berisi "Yes" = "Yes";. Jenis pengelolaan file pelokalan ini tampaknya aneh. Apalagi jika teks yang diterjemahkan panjang maka ID juga panjang. Kapan pun karakter nilai teks standar diubah, maka ID terjemahan juga akan berubah. Ini mengarah ke masalah ketika sistem terjemahan eksternal digunakan. Mengubah ID terjemahan dipahami sebagai menambahkan teks terjemahan baru, yang mungkin tidak selalu diinginkan.

petrsyn
sumber
2

Saya menulis sebuah skrip untuk membantu menjaga Localizable.strings dalam berbagai bahasa. Meskipun tidak membantu dalam pelengkapan otomatis, membantu menggabungkan file .strings menggunakan perintah:

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

Untuk info lebih lanjut, lihat: https://github.com/hiroshi/merge_strings

Beberapa dari Anda merasa bermanfaat, saya harap.

hiroshi
sumber
2

Jika ada yang mencari solusi Swift. Anda mungkin ingin memeriksa solusi yang saya buat di sini: SwiftyLocalization

Dengan beberapa langkah untuk menyiapkan, Anda akan memiliki pelokalan yang sangat fleksibel di Google Spreadsheet (komentar, warna kustom, sorot, font, beberapa lembar, dan banyak lagi).

Singkatnya, langkah-langkahnya adalah: Google Spreadsheet -> file CSV -> Localizable.strings

Selain itu, itu juga menghasilkan Localizables.swift, sebuah struct yang bertindak seperti antarmuka untuk pengambilan kunci & decoding untuk Anda (Anda harus secara manual menentukan cara untuk memecahkan kode String dari kunci sekalipun).

Kenapa ini bagus?

  1. Anda tidak perlu lagi memiliki kunci sebagai string biasa di semua tempat.
  2. Kunci yang salah terdeteksi pada waktu kompilasi.
  3. Xcode dapat melakukan pelengkapan otomatis.

Meskipun ada alat yang dapat melengkapi secara otomatis kunci Anda yang dapat dilokalkan. Referensi ke variabel nyata akan memastikan bahwa itu selalu merupakan kunci yang valid, kalau tidak itu tidak akan dikompilasi.

// 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)

Proyek ini menggunakan Google App Script untuk mengonversi Sheets -> CSV, dan script Python untuk mengonversi file CSV -> Localizable.strings Anda dapat melihat sekilas lembar contoh ini untuk mengetahui apa yang mungkin.

aunnnn
sumber
1

dengan iOS 7 & Xcode 5, Anda harus menghindari menggunakan metode 'Localization.strings', dan menggunakan metode 'lokalisasi basis' baru. Ada beberapa tutorial di sekitar Anda jika Anda mencari 'basis lokalisasi'

Apple doc: Pelokalan basis

Ronny Webers
sumber
ya Steve itu benar. Selain itu, Anda masih memerlukan metode file .strings untuk string apa pun yang dihasilkan secara dinamis. Tetapi hanya untuk ini, metode yang disukai Apple adalah lokalisasi basis.
Ronny Webers
Tautan ke metode baru?
Hyperbole
1
Imo basis metode Pelokalan tidak berharga. Anda masih harus menyimpan file lokasi lain untuk string dinamis, dan itu membuat string Anda menyebar melalui banyak file. String di dalam Nibs / Storyboards dapat dilokalkan secara otomatis ke kunci di Localizable.strings dengan beberapa Libs seperti github.com/AliSoftware/OHAutoNIBi18n
Rafael Nobre
0
#define PBLocalizedString(key, val) \

[[NSBundle mainBundle] localizedStringForKey:(key) value:(val) table:nil]
baozhifei
sumber
0

Saya sendiri, saya sering terbawa dengan pengkodean, lupa untuk memasukkan entri ke dalam file .strings. Jadi saya punya skrip pembantu untuk menemukan apa yang harus saya kembalikan ke file .strings dan terjemahkan.

Ketika saya menggunakan makro saya sendiri di atas NSLocalizedString, tinjau dan perbarui skrip sebelum menggunakan seperti yang saya asumsikan untuk kesederhanaan bahwa nil digunakan sebagai param kedua untuk NSLocalizedString. Bagian yang ingin Anda ubah adalah

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

Ganti saja dengan sesuatu yang cocok dengan penggunaan makro dan NSLocalizedString Anda.

Inilah skripnya, Anda hanya perlu Bagian 3 saja. Sisanya adalah untuk melihat lebih mudah dari mana semua itu berasal:

// 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

File output berisi kunci yang ditemukan dalam kode, tetapi tidak di file Localizable.strings. Berikut ini contohnya:

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

Tentu saja bisa dipoles lebih banyak, tetapi saya pikir saya akan berbagi.

Stanislav Dvoychenko
sumber