ინტერნაციონალიზაციის დეველოპერის გზამკვლევი
ასე რომ, თუ ფუნქცია, რომელზეც მუშაობთ, მოითხოვს რაიმე ტექსტს, ის ალბათ დაკავშირებულია i18n-თან. ეს სახელმძღვანელო დაგეხმარებათ ამ მოგზაურობაში და მოიცავს:
- მაღალი დონის სისტემის მიმოხილვა
- საუკეთესო პრაქტიკა და ანტი-ნიმუშები
- მაგალითები
დავიწყოთ სწრაფი მაღალი დონის მიმოხილვით.
საძირკვლის ყუთები
ჩვენ გვაქვს ორი საძირკველი ყუთი კომუნალური საშუალებებით, რომელთა გამოყენება დაგჭირდებათ:
client/i18n
ჩვენი მთარგმნელობითი შესაძლებლობების ხორცი და სისხლი.
ის ეფუძნებაfluent-rsყუთს დაassets_manager-ის შეფუთვას, რომელიც გვაქვსcommon/assets-ში.
ყველაზე სასარგებლო ტიპი ამ ყუთიდან არის ლოკალიზაცია რომელიც ავლენსget_msg-ოჯახის მეთოდებს, რომლებიც იღებენ i18n კლავიშს დაget_contentმეთოდს რომელსაც შეუძლია მიიღოსContentობიექტი. ორივე გაძლევთ მზა ნათარგმნ სტრიქონს გამოსაყენებლად.common/i18n
მართავს ინტერფეისებს ლოკალიზაციისთვის, როცა ამის გაკეთება გჭირდებათvoxygen-ის გარეთ.
რაც მთავარიაContentტიპი, რომელიც არის რეკურსიული ტიპი, რომელიც წარმოადგენს i18n-ის დაგვიანებულ შეფასებას საჭირო კონტექსტით.
მინიშნება: თქვენ არ გჭირდებათ Content, თუ უკვე ხართ voxygen-ში. უბრალოდ გამოიყენეთ get_msg ან get_msg_ctx.
ფუნდამენტური ყუთების აზრი იმაში მდგომარეობს, რომ ისინი ძალიან მცირეა, სწრაფად შედგენილია და ბევრად უფრო აბსტრაქტული.
აპლიკაციის ყუთები
ახლა, როდესაც იცით რა შეგიძლიათ გამოიყენოთ თარგმანისთვის, მოდით ვისაუბროთ ყუთებზე სად შეგიძლიათ გამოიყენოთ იგი:
voxygen\
- კლიენტის ყუთი. მართავს გრაფიკას, აუდიოსა და ინტერფეისს. და UI იყენებს ტექსტს, როგორც თქვენ შეგიძლიათ გამოიცნოთ.
ყველა ვიჯეტს უნდა ჰქონდეს წვდომა იქi18n::Localization-ზე, ასე რომ, თუ თარგმნილი სტრიქონი გჭირდებათ, გააკეთეთ.
serverდა თანამშრომელი
დიდი განსხვავებაა, თუ რომელ კოლოფს მუშაობთ, რადგან მათ არ აქვთ წვდომაLocalizationობიექტზე.
მათ არც კი იციან რა ენას იყენებენ მოთამაშეები.
ამის ნაცვლად, თუ თქვენ აწარმოებთ შეტყობინებების სერვერის მხარეს, როგორიცაა NPC შეტყობინება, ბრძანების შედეგი, NPC სახელი, თქვენ უნდა გამოიყენოსContent.
აქტივები
კარგია, რომ შეგიძლია დააკოდირო რაც გინდა, მაინც გჭირდება აქტივები.
მოდით შევამოწმოთ ისინი:
assets/voxygen/i18n/
საქაღალდე, სადაც ყველა Fluent ფაილი ცხოვრობს. თუ გსურთ დაამატოთ ახალი სტრიქონი, აირჩიეთ ფაილი, რომელიც კარგად გამოიყურება, დადე ახალი გასაღები და კარგად ხარ.
თუ ფიქრობთ, რომ გჭირდებათ ახალი ფაილის შექმნა, გთხოვთ, მიმართოთ მთარგმნელობით ჯგუფს.assets/common/
ინახავს სხვადასხვა ფაილებს, რომლებიც საკმაოდ შეიცავსContentიდენტიფიკატორებს:assets/common/entity/**/*.ronიყენებსname: Translated-ს i18n კონტენტზე მითითებისთვის.assets/common/npc_names.ronიყენებსgeneric: <i18n-key>სახელების თარგმანების მისაღებად.assets/common/item_i18n_manifest.ronარის ასახვა ერთეულის def-დან მის თარგმანამდე.- და ა.შ
საუკეთესო პრაქტიკა და რეკომენდაცია
კარგი, ამას თამამად დავწერ: ** შეინახე მარტივი **
თუ სხვა არაფერი გახსოვთ, დაიმახსოვრე ეს.
Fluent არის ძლიერი ფორმატი, რადგან ლოკალიზაცია რთულია. ჩვენ ** უნდა** მივაწოდოთ ყველაზე მარტივი
ინტერფეისი მთარგმნელებთან და საკუთარი აბსტრაქციების დანერგვა და ხელახლა გამოსაყენებელი ნაწილების ზემოდან მოთხოვნილება იქნება
გამოიწვიოს შეცდომები.
ეს ეწინააღმდეგება პროგრამირების ტრადიციულ წესებს, სადაც ჩვენ ვცდილობთ დავწეროთ რაც შეიძლება ნაკლები კოდი და შეძლებისდაგვარად ხელახლა გამოყენება, მაგრამ ეს არ მუშაობს თარგმანებზე, რადგან ზუსტად არ ვიცით რა ხელახლა გამოსაყენებლად.
ნუ აერთიანებთ
ფლუენტის დოკუმენტების მითითება.
# ❌ Avoid splitting messages.
reaction-thumbs-up = thumbs up
reaction-smiley-face = smiley face
reacted-with = { $user_name } has reacted with a { $reaction_type }.
ეს ინგლისურად სწორ შეტყობინებას ჰგავს, მაგრამ ის შეიძლება სწრაფად დაიშალა.
რა მოხდება, თუ დაამატებთ რეაქციას „თვალის გახვევაზე“, რომელიც უნდა დაიწყოს an-ით და არა a-ით?
ასეთი სიტუაციების თავიდან აცილების მიზნით, აქ არის პირველი წესი თქვენთვის: არ შეერთოთ
იგივე შეიძლება გადაიწეროს შემდეგნაირად.
# ✅ Redundancy helps localizers understand each message in full.
reacted-with-thumbs-up = { $user_name } has reacted with a thumbs up.
reacted-with-smiley-face = { $user_name } has reacted with a smiley face.
reacted-with-eye-roll = { $user-name } has reacted with an eye-roll.
ამ წესიდან არის გამონაკლისები. თუ სხვაგვარად გექნებოდათ სტრიქონების კომბინაციური რაოდენობა, იქნებით
იძულებული გახდა გამოიყენოს შეერთება, მაგრამ გთხოვთ იყოთ ძალიან ფრთხილად და მიმართოთ მთარგმნელობით ჯგუფს.
და თუ თქვენ გჭირდებათ შეერთების გამოყენება, გახსენით შაბლონი, რათა მთარგმნელებს მაქსიმალური კონტროლი მისცეთ.
არ გამოიყენოთ სელექტორები ლოგიკისთვის
# ❌ Don't use selectors for logic
dialogue-buy_hire_days = { $days ->
[1] A day
[7] A week
*[other] { $days } days
}
ოქროს წესი არის ის, რომ სელექტორების ყველა ვარიანტს ზუსტად იგივე მნიშვნელობა უნდა ჰქონდეს.
თუ გსურთ სადმე ლოგიკის ჩასმა, ჩადეთ ის თქვენს კოდში და დარეკეთ მარჯვენა კლავიშს.
# ✅ Uses attributes for logic, exposes selectors to handle plurals.
dialogue-buy_hire_days =
.day = A day
.week = A week
.unknown = { $days ->
[one] { $days } day
*[other] { $days } days
}
ყურადღება მიაქციე სქესს
ჩვენ ახლახან დავიწყეთ ამის გაკეთება, მაგრამ თუ ეს შესაძლებელია, შეეცადეთ მოგვაწოდოთ გენდერული სტრიქონი.
hud-chat-online_msg =
{ $user_gender ->
[she] [{ $name }] si è connessa.
*[he] [{ $name }] si è connesso.
}
ამის კოდი დაახლოებით ასე გამოიყურება.
// getting the string
let gender_str = |uid: &Uid| {
if let Some(pi) = info.player_info.get(uid) {
match pi.character.as_ref().and_then(|c| c.gender) {
Some(Gender::Feminine) => "she".to_owned(),
Some(Gender::Masculine) => "he".to_owned(),
_ => "??".to_owned(),
}
} else {
"??".to_owned()
}
};
// usage
localization
.get_msg_ctx("hud-chat-online_msg", &i18n::fluent_args! {
"user_gender" => gender_str(uid),
"name" => name_format(uid),
})
მნიშვნელოვანი ბიტი არის player_info-ში, რომელიც შეიცავს character-ს, რომელიც შეიცავს სქესს.
ამ დროისთვის ჩვენ მხოლოდ ორ სქესს ვუჭერთ მხარს, რაც სამწუხაროა, მაგრამ იმედია მალე გავაუმჯობესებთ.
მიეცით კარგი მაგალითი
თარჯიმნებს აქვთ ცოდნის განსხვავებული დონე, ამიტომ შეეცადეთ შექმნათ რაიმე ზედმეტი ინგლისურ ვერსიებში.
# ❌ [one] doesn't mean *single* item
# ❌ Concatenation makes it harder to see whole picture
hud-trade-buy = Buy Price: { $coin_num ->
[one] one coin
*[other] { $coin_formatted } coins
}
მიუხედავად იმისა, რომ ისევ სწორი სტრიქონია, ყოველ შემთხვევაში, როდესაც საქმე ეხება ინგლისურს, მას აქვს გარკვეული პრობლემები.
# ✅ Uses [1] for exact match
# ✅ String isn't interrupted by selectors, always full.
hud-trade-buy = { $coin_num ->
[1] Buy Price: one coin
*[other] Buy Price: { $coin_formatted } coins
}
მაგალითად, უკრაინულ ენაზე თარგმნის სწორი გზა იქნება შემდეგი.
hud-trade-buy =
{ $coin_num ->
[1] Ціна купівлі: одна монета
[one] Ціна купівлі: { $coin_formatted } монета
[few] Ціна купівлі: { $coin_formatted } монети
[many] Ціна купівлі: { $coin_formatted } монет
*[other] Ціна купівлі: { $coin_formatted } монет
}
მაგრამ თუ ადამიანი, რომელსაც სრულად არ ესმის წესები, დააკოპირებს პირველ მაგალითს, ჩვენ შეგვიძლია მივიღოთ რაღაც მსგავსი
# ❌ 21 would mean [one] as well, while we assume [one] means 1 only
hud-trade-buy = { $coin_num ->
[one] Ціна купівлі: 1 монета
*[other] Ціна купівлі: { $coin_formatted } монет
}
და რადგან უკრაინულში რაღაცეები, როგორიცაა “21” მიჰყვება იგივე წესებს, რაც “1”, ის მიეკუთვნება კატეგორიას
[one]-დან, ჩვენ მივიღებთ “შესყიდვის ფასს: 1 მონეტა”, რაც აშკარად არასწორია, რადგან, კარგად
“21” არ არის “1”.
ასე რომ, გთხოვთ, გაითვალისწინოთ ეს ინგლისური სტრიქონების მოწოდებისას.
მაგალითები
GUI
ვთქვათ, გსურთ გამაფრთხილებელი შეტყობინების ინტერნაციონალიზაცია, როდესაც რაიმე აბსტრაქციაა აიძულებს ნივთების ჩამოგდებას.
თქვენ დაიწყებდით გამართულ ფაილში გასაღების დამატებით, assets/voxygen/i18n/en/hud/bag.ftl.
hud-bag-swap_slots_drop_items = { $slot_deficit ->
[1] This will result in dropping 1 item on the ground. Are you sure?
*[other] This will result in dropping { $slot_deficit } items on the ground. Are you sure?
}
შემდეგ იღებთ ფუნქციას, რომელიც მოელის სტრიქონს და იყენებთ LocalizationHandle::get_msg_ctx მასზე.
self.hud.set_prompt_dialog(PromptDialogSettings::new(
global_state.i18n.read().get_msg_ctx(
"hud-bag-use_slot_equip_drop_items",
&i18n::fluent_args! {
"slot_deficit" => slot_deficit.unsigned_abs(),
})
),
),
p.s. თუ თქვენ მიიღებთ შეცდომებს მთელი ცხოვრების განმავლობაში, ეს არის Rust, ეს ხდება, ცეკვა სესხის შემმოწმებელთან არ არის მოცემული სახელმძღვანელო, შეიძლება ფერიის თქვენთან ერთად იყოს.
სერვერის შეტყობინება
კარგი, ეს არის ძალიან მარტივი ფუნქცია, მაგრამ ის საშუალებას გვაძლევს ვაჩვენოთ, როგორ გამოიყენებდით Content ტიპს.
fn handle_version(
server: &mut Server,
client: EcsEntity,
_target: EcsEntity,
_args: Vec<String>,
_action: &ServerChatCommand,
) -> CmdResult<()> {
server.notify_client(
client,
ServerGeneral::server_msg(
ChatType::CommandInfo,
Content::localized_with_args("command-version-current", [
("hash", (*common::util::GIT_HASH).to_owned()),
("date", (*common::util::GIT_DATE).to_owned()),
]),
),
);
Ok(())
}
ServerGeneral::server_msg ფუნქცია იღებს Content, რადგან ჩვენ გვჭირდება ლოკალიზაცია, მაგრამ ვერ გამოვიყენებთ LocalizationHandle-ს.
აქ ჩვენ ვიყენებთ Content::localized_with_args, რომელიც იღებს გასაღებს, რომლის ლოკალიზებაც გსურთ და არგუმენტების ჩამონათვალს (გასაღები, მნიშვნელობა).
და რა თქმა უნდა, თუ ჩვენ განვსაზღვრავთ ბრძანებას, ასევე უნდა მივცეთ აღწერა, რისთვისაც შეგიძლიათ გამოიყენოთ Content ასევე.
ServerChatCommand::data() ფუნქციის სიღრმეში თქვენ უნდა დაამატოთ ეს.
ServerChatCommand::Version => {
cmd(vec![], Content::localized("command-version-desc"), None)
},
ბრძანებების უმეტესობას აქვს უფრო რთული მონაცემები, მაგრამ /version ძალიან მარტივია, ჩვენ მხოლოდ მისი აღწერილობის განსაზღვრა გვჭირდება.
და რადგან მას არავითარი კონტექსტი არ სჭირდება, ჩვენ შეგვიძლია უბრალოდ გამოვიყენოთ Content::localized და არა Content::localized_with_args.
დაბოლოს, რაც არანაკლებ მნიშვნელოვანია, არ დაგავიწყდეთ ორივე სტრიქონის დამატება assets/voxygen/i18n/en/command.ftl-ში.
command-version-desc = Prints server version
command-version-current = Server is running { $hash }[{ $date }]
დასკვნა
სულ ესაა. ვეცადე ყველაფერი დამეფარა, მაგრამ რაღაცეები გამომრჩა ალბათ, ასე რომ არ მოგერიდოს კითხვა.
ზოგადად, MR-ის გაკეთებამდე, კატეგორიულად გირჩევთ, დაუკავშირდეთ ვინმეს და * ისაუბროთ*.
ამასთან დაკავშირებით, დოკუმენტაციის დაწერა რთულია და დახმარება ყოველთვის დასაფასებელია. თუ რამეა
გაუგებარია, თქვენ შეგიძლიათ გააუმჯობესოთ იგი. თქვენ არ გჭირდებათ ყველაფერი იცოდეთ, რომ დაგეხმაროთ, უბრალოდ უნდა იყოთ მოთმინება.