Đến nội dung

Ngôn ngữ ​

Thông báo của ứng dụng có thể được hiển thị bằng ngôn ngữ của những người sử dụng ứng dụng đó, trong giao diện native và trong các trang, với myzig.i18n (sau gói của mygo i18n). Thông báo của nó nằm trong danh mục, một tệp cho mỗi ngôn ngữ, được nhúng tại thời điểm xây dựng và được kiểm tra tại thời điểm biên dịch: khóa mà danh mục thiếu, đối số mà cuộc gọi bỏ qua hoặc đưa ra loại sai, bản dịch có khóa hoặc đối số mà nguồn của nó không có hoặc thông báo không phân tích cú pháp, là lỗi biên dịch, không phải là điều bất ngờ trong quá trình sản xuất.

zig
const i18n = myzig.i18n;

const L = i18n.Catalog(.{
    .source = "en",
    .files = .{
        .en = @embedFile("locales/en.json"),
        .de = @embedFile("locales/de.json"),
    },
});

fn view(s: *App, c: *ui.Context) void {
    ui.heading(c, L.text(.title));
    ui.text(c, L.t(c, .filesSelected, .{ .count = s.selected.len }), .{});
}

Không có gì trong số đó được liên kết với một ứng dụng không sử dụng nó và dữ liệu ngày chỉ được định dạng ngày.

Danh mục ​

Danh mục là một tệp ARB (JSON, dưới dạng Flutter và Chrome) cho mỗi ngôn ngữ, được đặt tên theo thẻ BCP 47 chuẩn của ngôn ngữ đó: en.json, de.json, zh-Hant.json, pt-BR.json. Mỗi phím đặt tên cho một thông điệp; các khóa bắt đầu bằng @ mô tả thông báo của khóa sau chúng và @@locale, nếu được cung cấp, phải là ngôn ngữ của tệp:

json
{
  "@@locale": "en",
  "title": "Notes",
  "@title": { "description": "The window's title." },
  "greeting": "Hello, {name}!",
  "@greeting": { "placeholders": { "name": { "type": "String", "example": "Ada" } } },
  "filesSelected": "{count, plural, =0 {No files selected} one {# file selected} other {# files selected}}",
  "@filesSelected": { "placeholders": { "count": { "type": "int" } } },
  "price": "Total: {amount, number, ::.00}",
  "@price": { "placeholders": { "amount": { "type": "double" } } },
  "due": "Due {day, date, medium}"
}

Khóa là một chữ cái, sau đó là các chữ cái, chữ số và _; khóa <namespace>.<key> thay đổi thông báo của thư viện (myzig.undo, bên dưới). Danh mục nguồn (tùy chọn source, tiếng Anh theo mặc định) có mọi khóa; bản dịch có thể thiếu một số bản dịch, sau đó sẽ bị lùi lại.

.files là cấu trúc của một trường cho mỗi ngôn ngữ, như trên hoặc một lát cặp .{ "de", @embedFile("locales/de.json") }. .pseudo = true thêm các ngôn ngữ giả (bên dưới).

Các loại đối số ​

type của phần giữ chỗ là String, int, double hoặc num (một số), DateTime (một i18n.DateTime) hoặc Object. Một đối số không có đối số sẽ có kiểu từ cách thông báo sử dụng nó: được tính bằng plural nó là một số, được viết bởi date một ngày. Một cuộc gọi đưa ra một đối số thuộc loại khác không được biên dịch:

zig
L.text(.titel)                                  // no member 'titel' in Key
L.text(.greeting)                               // greeting has arguments
try L.printIn(w, "de", .greeting, .{})          // needs the argument name
try L.printIn(w, "de", .filesSelected, .{ .count = "3" })   // takes an integer

Tin nhắn ​

Tin nhắn là ICU MessageFormat:

Cú phápNó viết gì
{name}một cuộc tranh luận
{n, plural, =0 {...} one {# file} other {# files}}trường hợp theo quy tắc số nhiều của CLDR về ngôn ngữ của tin nhắn; # là số
{n, selectordinal, one {#st} two {#nd} few {#rd} other {#th}}một trường hợp theo quy tắc thứ tự
{gender, select, female {She} male {He} other {They}}một trường hợp bằng một chuỗi
{amount, number}, {amount, number, ::.00}, {p, number, percent}, integer, ::group-offmột số ở địa phương, có kiểu dáng hoặc khung
{day, date, short|medium|long|full}, {t, time, short}ngày hoặc giờ ở địa phương
'{name}', ''văn bản được trích dẫn, một dấu nháy đơn

Các trường hợp số nhiều tuân theo quy tắc của CLDR cho mọi ngôn ngữ mà CLDR có (i18n.cardinalCategory, i18n.ordinalCategory, CLDR i18n.cldr_version): few và many trong tiếng Nga, sáu trong tiếng Ả Rập, v.v. i18n.exact("1.50") chuyển một số thập phân giữ các chữ số phân số của nó, có loại số nhiều là hai chữ số phân số.

Trong Zig ​

GọiNó làm gì
L.text(.key)thông báo của một khóa không có đối số, bằng ngôn ngữ mà ứng dụng hiển thị
L.textIn(locale, .key)tương tự trong locale
L.t(c, .key, args)tin nhắn cho giao diện native, trong bộ nhớ của khung c
L.print(w, .key, args), L.printIn(w, locale, .key, args)ghi nó vào std.Io.Writer
L.alloc(gpa, .key, args), L.allocIn(gpa, locale, .key, args)phân bổ nó
L.bufPrintIn(buf, locale, .key, args)ghi nó vào bộ đệm

L.languages liệt kê các ngôn ngữ của danh mục, nguồn trước tiên và L.missing các khóa mà mỗi ngôn ngữ thiếu, dành cho một bài kiểm tra muốn toàn bộ mọi bản dịch:

zig
test "every translation is whole" {
    inline for (L.missing, 0..) |m, i| {
        if (m.len > 0) std.debug.print("{s} lacks {s}\n", .{ L.languages[i], m[0] });
        try std.testing.expectEqual(@as(usize, 0), m.len);
    }
}

i18n.formatMessage(w, locale, "message", args) viết thông báo được cung cấp trong mã (được phân tích cú pháp tại thời điểm biên dịch) thay vì trong danh mục.

Ngôn ngữ ứng dụng hiển thị ​

Toàn bộ ứng dụng hiển thị một ngôn ngữ, i18n.locale() ("en" cho đến khi ứng dụng đặt một ngôn ngữ). Ứng dụng đặt nó với i18n.setLocale, trên luồng chính, từ ngôn ngữ của người dùng và danh mục của nó, ngôn ngữ phù hợp nhất với người dùng:

zig
const langs = try myzig.system.languages(gpa);
defer myzig.system.freeList(gpa, langs);
i18n.setLocale(i18n.matchedLanguage(langs, L.languages));

i18n.locale() là ngôn ngữ mà ứng dụng hiển thị, không phải của người dùng: một ứng dụng tự dịch văn bản của nó, không cần danh mục, nhận "en" từ nó cho đến khi nó gọi setLocale và đọc ngôn ngữ của người dùng từ myzig.system.languages. (mygoApp.Locale() đã thay đổi nên ở mức 0,5: trước đó, nó trả về ngôn ngữ ưa thích đầu tiên của người dùng bất kể ứng dụng được viết bằng gì.)

i18n.match(preferred, supported) trả về chỉ mục phù hợp nhất hoặc null: ngôn ngữ và khu vực trước tiên (en-GB cho en-AU, cả hai en-001), sau đó chỉ ngôn ngữ, sau đó là khu vực thông thường của nó, sau đó là bất kỳ ngôn ngữ nào khác. Ngôn ngữ mà người đọc thích nhiều hơn sẽ giành chiến thắng so với kết quả phù hợp gần hơn với ngôn ngữ mà họ ít thích hơn: fr cho fr-CA thắng de cho de. matchedLanguage quay lại ngôn ngữ được hỗ trợ đầu tiên, nguồn của ứng dụng.

Thông báo sẽ phân giải bằng ngôn ngữ được hiển thị, các ngôn ngữ dự phòng của nó theo ngôn ngữ mẹ của CLDR (i18n.parent, i18n.chain: en-AU, en-001, en), sau đó là ngôn ngữ nguồn. Các luồng khác tự đặt tên cho một ngôn ngữ (printIn, textIn).

Khi ngôn ngữ thay đổi ​

setLocale đếm một thế hệ, i18n.localeGeneration(), những view giữ văn bản so sánh để biết nó đã thay đổi. Giao diện native xây dựng mọi frame từ trạng thái, do đó, view cho biết các thông báo của nó với L.text và L.t hiển thị ngôn ngữ mới trong khung tiếp theo; hãy yêu cầu một cái có redraw của cửa sổ sau khi cài đặt nó.

Phải sang trái ​

i18n.direction(tag) cho biết liệu một ngôn ngữ có được viết từ phải sang trái hay không (tiếng Ả Rập, tiếng Do Thái, tiếng Ba Tư, tiếng Urdu, theo chữ viết của chúng) và ui.direction.fromLocale(c, i18n.locale()) hiển thị một cửa sổ giao diện native ra ngoài (xem Phải sang trái).

Định dạng khu vực ​

myzig.system.locale(gpa) là ngôn ngữ của người dùng ("en-US") và myzig.system.region(gpa) khu vực của họ ("US"). i18n.regionFor(locale) đưa ra i18n.Region: ngôn ngữ của nó, lịch ngày bắt đầu tuần của chúng vào (first_weekday, 0 Chủ Nhật đến 6 Thứ Bảy) và chu kỳ giờ của nó (12 hoặc 24). i18n.firstWeekday(locale), i18n.hourCycle(locale) và các phần mở rộng -u-fw và -u-hc của thẻ đều có nội dung tương tự; i18n.autonym(locale) đặt tên cho một ngôn ngữ ("Deutsch", "简体中文"), cho một menu ngôn ngữ.

Những con số và ngày tháng ​

Các số và ngày được viết theo cách ICU và Intl viết, với dữ liệu CLDR cho 62 ngôn ngữ và những dữ liệu liên quan đến chúng: dấu phân cách, nhóm, chữ số, tên tháng và ngày trong tuần, mẫu theo kiểu và khung, 12 hoặc 24 giờ.

zig
var buf: [64]u8 = undefined;
_ = try i18n.bufFormatNumber(&buf, "de", 1234.5, .{});                  // "1.234,5"
_ = try i18n.bufFormatNumber(&buf, "en", 0.25, .{ .percent = true });   // "25%"
_ = try i18n.bufFormatNumber(&buf, "en", 1.5, .{ .min_fraction = 2 });  // "1.50"

const t = i18n.DateTime.utc(2026, 1, 5, 13, 7, 9);
_ = try i18n.bufFormatDate(&buf, "en", t, .{ .date = .medium });        // "Jan 5, 2026"
_ = try i18n.bufFormatDate(&buf, "en", t, .{ .time = .short });         // "1:07 PM"
_ = try i18n.bufFormatDate(&buf, "en", t, .{ .skeleton = "MMMd" });     // "Jan 5"

i18n.NumberOptions có percent, integer, min_fraction, max_fraction, min_integer, grouping và half_even (làm tròn một nửa thành số chẵn, như MessageFormat của ICU đã làm, thay vì cách xa 0 như Intl). i18n.DateOptions có các kiểu date và time (.short, .medium, .long, .full), CLDR skeleton ("yMMMd", "MMMd", "Ed", "jm", ... của i18n.date.skeletons) và hour_cycle (12 hoặc 24 để làm theo cài đặt của người dùng). i18n.DateTime là số giây kể từ năm 1970 trong UTC với offset_minutes tùy chọn trong vùng của nó. formatNumber và formatDate ghi vào std.Io.Writer.

i18n.months(locale, width, stand_alone), i18n.weekdays(...) và i18n.dayPeriods(locale, width) đặt tên theo i18n.Width (.wide, .abbreviated, .short, .narrow), theo yêu cầu của lịch.

Chuỗi riêng của myzig ​

Nội dung điều khiển và menu của myzig ("Hoàn tác", "Tháng tới", "Ghi chú thoát", tên mà công nghệ hỗ trợ ghi) đến từ myzig.l10n, bằng ngôn ngữ mà ứng dụng hiển thị: tiếng Anh và bản dịch sang tiếng Trung giản thể, phồn thể và tiếng Nhật (src/l10n/en.json, zh-Hans.json, zh-Hant.json, ja.json, từ danh mục của mygo).

Một bản dịch chỉ được biên dịch cho một ứng dụng gửi ngôn ngữ của nó, vì vậy một ứng dụng không mang theo ngôn ngữ nào mà nó không hiển thị: những bản dịch trong danh mục của ứng dụng, tên tệp gốc của nó và bất kỳ tên nào mà nó tự đặt tên:

zig
// in main.zig
pub const myzig_catalog = L;              // an app with zh-TW.json gets zh-Hant
pub const myzig_languages = .{"zh-Hans"}; // and these, besides

Một ứng dụng thay đổi chuỗi của myzig hoặc dịch chúng sang các ngôn ngữ khác của nó bằng các khóa myzig.<key> trong danh mục của ứng dụng:

json
{
  "myzig.undo": "Rückgängig",
  "myzig.menuQuitApp": "{app} beenden"
}

Một chuỗi phân giải bằng ngôn ngữ của ứng dụng, các ngôn ngữ dự phòng và ngôn ngữ nguồn của ứng dụng trong số các phần ghi đè của ứng dụng, sau đó là trong số các bản dịch của myzig, sau đó là bằng tiếng Anh. Nói một cái sẽ tốn một lần tra cứu và không phân bổ gì (myzig.l10n.text(.undo), textIn(locale, key), printIn(w, locale, key, args)).

Đang thử một ngôn ngữ ​

Với .pseudo = true, một danh mục có hai ngôn ngữ giả, được tạo từ thông báo của nguồn, để xem bố cục của giao diện trước khi được dịch:

  • en-XA (i18n.pseudo_accents) nhấn mạnh các chữ cái, làm cho văn bản dài hơn khoảng 40% và đặt dấu ngoặc vuông ("[Šéļéçţ ƒîļéš one two]"), để tìm các chuỗi không có trong danh mục, bị cắt hoặc được ghép lại với nhau từ các mảnh.
  • ar-XB (i18n.pseudo_bidi) hiển thị từng từ từ phải sang trái và ứng dụng được bố trí từ phải sang trái để tìm những từ không phản chiếu.
zig
i18n.setLocale(i18n.pseudo_accents);

i18n.isPseudo(tag) phân biệt chúng. Họ sử dụng các quy tắc số nhiều của tiếng Anh.

Trong trang ​

Thông báo của danh mục đến các trang thông qua client được tạo (myzig.js, myzig.d.ts; xem Bindings), với chức năng được nhập cho mỗi khóa cho biết thông báo bằng ngôn ngữ của trang với Intl của trình duyệt, theo cùng thông báo, trường hợp và dự phòng như trong Zig. Các tin nhắn nằm trong client, vì vậy lần sơn đầu tiên có ngôn ngữ phù hợp. Đăng ký danh mục khi ứng dụng đăng ký sự kiện:

zig
try app.registerEvent(i18n.Page(L)); // adds `messages` to the client
ts
import { messages, messagesIn, languages, onLocaleChange } from "./myzig.js";

heading.textContent = messages.filesSelected(3);
messagesIn("de").greeting("Ada"); // "Hallo, Ada!"
onLocaleChange((locale) => render());

messages cho biết từng thông báo bằng ngôn ngữ của trang: window.myzig.locale khi được đặt, nếu không thì tài liệu là lang, nếu không thì là navigator.language. onLocaleChange lắng nghe sự kiện của trang myzig:locale; myzig chưa tự gửi sự kiện đó nên ứng dụng thay đổi ngôn ngữ của trang sẽ gửi sự kiện đó hoặc trang đó sẽ tự hiển thị lại.

Tham khảo gói ​

src/i18n/i18n.zig xuất khẩu: Catalog, Message, formatMessage, bufFormatMessage, Exact, exact; locale, setLocale, localeGeneration; match, matchOne, matchedLanguage, fallbacks, Fallbacks; Tag, normalize, parent, chain, base, maximize, likelyScript, likelyRegion, Direction, direction, isPseudo, pseudo_accents, pseudo_bidi; Plural, Operands, Categories, cardinalCategory, ordinalCategory, cardinalCategories, ordinalCategories; NumberOptions, formatNumber, bufFormatNumber; date, DateTime, DateOptions, Style, Width, formatDate, bufFormatDate, months, weekdays, dayPeriods; firstWeekday, hourCycle, autonym, Region, regionFor; Page; và message, trình phân tích cú pháp, dành cho các công cụ đọc tin nhắn tại thời điểm biên dịch.

Các bảng quy tắc số nhiều, số và ngày tháng được tạo từ CLDR bằng các tập lệnh trong src/i18n/gen/ và được kiểm tra dựa trên kết quả đầu ra của chính ICU (src/i18n/tests_*).

Phát hành theo giấy phép MIT.