Chuyển đến nội dung chính

Bài đăng

Hiển thị các bài đăng có nhãn i18n

RTL layouts that do not look broken in Flutter — hướng dẫn Flutter

Half the world reads right-to-left. Mirroring is not optional. Bài này chuyển quan sát đó thành một mô hình nhỏ để bạn có thể kiểm thử, đo lường và giữ cho code dễ bảo trì khi app lớn lên. Vấn đề cốt lõi Directionality, icons, and mirrored gestures. Câu hỏi hữu ích không phải API hay pattern có đẹp riêng lẻ hay không, mà là state nằm ở đâu, boundary nào chịu trách nhiệm khi lỗi xảy ra, và người dùng phục hồi thế nào khi happy path biến mất. Mô hình thực tế Use start/end, not left/right, in padding and alignment. Hãy bắt đầu bằng một owner rõ ràng cho behavior. Widget chỉ nên render và phát intent; IO, persistence, permission và retry nên nằm sau một interface nhỏ. Nhờ vậy bạn có seam để fake trong test và một chỗ ghi lại các dữ kiện cần theo dõi ở production. Trong app Flutter, boundary thường có dạng: Widget phát intent như load, submit, refresh hoặc retry. Controller hoặc use case validate intent rồi gọi boundary. Boundary trả về data có kiểu hoặc failure có kiểu, không tr...

RTL layouts that do not look broken in Flutter

Half the world reads right-to-left. Mirroring is not optional. This article turns that observation into a small implementation model you can test, measure, and keep boring when the app grows. The problem in one sentence Directionality, icons, and mirrored gestures. The useful question is not whether the API or pattern looks elegant in isolation. It is where state lives, which boundary owns failure, and how a user recovers when the happy path disappears. A practical model Use start/end, not left/right, in padding and alignment. Start with one explicit owner for the behavior. Keep widgets responsible for rendering and user intent; keep IO, persistence, permissions, and retries behind a small interface. That gives you a seam for a fake in tests and a place to record the facts that matter in production. For a Flutter app, the boundary usually looks like this: The widget emits an intent such as load, submit, refresh, or retry. A controller or use case validates the intent and call...

Pseudolocales and expansion testing before translators — hướng dẫn Flutter

German and Vietnamese expand. Arabic flips. Test before paying translators. Bài này chuyển quan sát đó thành một mô hình nhỏ để bạn có thể kiểm thử, đo lường và giữ cho code dễ bảo trì khi app lớn lên. Vấn đề cốt lõi Finding truncation bugs without a full translation set. Câu hỏi hữu ích không phải API hay pattern có đẹp riêng lẻ hay không, mà là state nằm ở đâu, boundary nào chịu trách nhiệm khi lỗi xảy ra, và người dùng phục hồi thế nào khi happy path biến mất. Mô hình thực tế Pseudolocales catch overflow for free. Hãy bắt đầu bằng một owner rõ ràng cho behavior. Widget chỉ nên render và phát intent; IO, persistence, permission và retry nên nằm sau một interface nhỏ. Nhờ vậy bạn có seam để fake trong test và một chỗ ghi lại các dữ kiện cần theo dõi ở production. Trong app Flutter, boundary thường có dạng: Widget phát intent như load, submit, refresh hoặc retry. Controller hoặc use case validate intent rồi gọi boundary. Boundary trả về data có kiểu hoặc failure có kiểu, khô...

Pseudolocales and expansion testing before translators

German and Vietnamese expand. Arabic flips. Test before paying translators. This article turns that observation into a small implementation model you can test, measure, and keep boring when the app grows. The problem in one sentence Finding truncation bugs without a full translation set. The useful question is not whether the API or pattern looks elegant in isolation. It is where state lives, which boundary owns failure, and how a user recovers when the happy path disappears. A practical model Pseudolocales catch overflow for free. Start with one explicit owner for the behavior. Keep widgets responsible for rendering and user intent; keep IO, persistence, permissions, and retries behind a small interface. That gives you a seam for a fake in tests and a place to record the facts that matter in production. For a Flutter app, the boundary usually looks like this: The widget emits an intent such as load, submit, refresh, or retry. A controller or use case validates the intent and...

Flutter localization with ARB and gen_l10n that translators like — hướng dẫn Flutter

Ship the l10n pipeline early — retrofitting 200 strings is painful. Bài này chuyển quan sát đó thành một mô hình nhỏ để bạn có thể kiểm thử, đo lường và giữ cho code dễ bảo trì khi app lớn lên. Vấn đề cốt lõi Untranslated messages, plurals, and gender-aware copy. Câu hỏi hữu ích không phải API hay pattern có đẹp riêng lẻ hay không, mà là state nằm ở đâu, boundary nào chịu trách nhiệm khi lỗi xảy ra, và người dùng phục hồi thế nào khi happy path biến mất. Mô hình thực tế ARB + gen_l10n is the supported path; keep keys stable for translators. Hãy bắt đầu bằng một owner rõ ràng cho behavior. Widget chỉ nên render và phát intent; IO, persistence, permission và retry nên nằm sau một interface nhỏ. Nhờ vậy bạn có seam để fake trong test và một chỗ ghi lại các dữ kiện cần theo dõi ở production. Trong app Flutter, boundary thường có dạng: Widget phát intent như load, submit, refresh hoặc retry. Controller hoặc use case validate intent rồi gọi boundary. Boundary trả về data có kiểu h...

Flutter localization with ARB and gen_l10n that translators like

Ship the l10n pipeline early — retrofitting 200 strings is painful. This article turns that observation into a small implementation model you can test, measure, and keep boring when the app grows. The problem in one sentence Untranslated messages, plurals, and gender-aware copy. The useful question is not whether the API or pattern looks elegant in isolation. It is where state lives, which boundary owns failure, and how a user recovers when the happy path disappears. A practical model ARB + gen_l10n is the supported path; keep keys stable for translators. Start with one explicit owner for the behavior. Keep widgets responsible for rendering and user intent; keep IO, persistence, permissions, and retries behind a small interface. That gives you a seam for a fake in tests and a place to record the facts that matter in production. For a Flutter app, the boundary usually looks like this: The widget emits an intent such as load, submit, refresh, or retry. A controller or use case ...

RTL layouts that do not look broken in Flutter — hướng dẫn Flutter

Half the world reads right-to-left. Mirroring is not optional. Bài này chuyển quan sát đó thành một mô hình nhỏ để bạn có thể kiểm thử, đo lường và giữ cho code dễ bảo trì khi app lớn lên. Vấn đề cốt lõi Directionality, icons, and mirrored gestures. Câu hỏi hữu ích không phải API hay pattern có đẹp riêng lẻ hay không, mà là state nằm ở đâu, boundary nào chịu trách nhiệm khi lỗi xảy ra, và người dùng phục hồi thế nào khi happy path biến mất. Mô hình thực tế Use start/end, not left/right, in padding and alignment. Hãy bắt đầu bằng một owner rõ ràng cho behavior. Widget chỉ nên render và phát intent; IO, persistence, permission và retry nên nằm sau một interface nhỏ. Nhờ vậy bạn có seam để fake trong test và một chỗ ghi lại các dữ kiện cần theo dõi ở production. Trong app Flutter, boundary thường có dạng: Widget phát intent như load, submit, refresh hoặc retry. Controller hoặc use case validate intent rồi gọi boundary. Boundary trả về data có kiểu hoặc failure có kiểu, không tr...

Pseudolocales and expansion testing before translators — hướng dẫn Flutter

German and Vietnamese expand. Arabic flips. Test before paying translators. Bài này chuyển quan sát đó thành một mô hình nhỏ để bạn có thể kiểm thử, đo lường và giữ cho code dễ bảo trì khi app lớn lên. Vấn đề cốt lõi Finding truncation bugs without a full translation set. Câu hỏi hữu ích không phải API hay pattern có đẹp riêng lẻ hay không, mà là state nằm ở đâu, boundary nào chịu trách nhiệm khi lỗi xảy ra, và người dùng phục hồi thế nào khi happy path biến mất. Mô hình thực tế Pseudolocales catch overflow for free. Hãy bắt đầu bằng một owner rõ ràng cho behavior. Widget chỉ nên render và phát intent; IO, persistence, permission và retry nên nằm sau một interface nhỏ. Nhờ vậy bạn có seam để fake trong test và một chỗ ghi lại các dữ kiện cần theo dõi ở production. Trong app Flutter, boundary thường có dạng: Widget phát intent như load, submit, refresh hoặc retry. Controller hoặc use case validate intent rồi gọi boundary. Boundary trả về data có kiểu hoặc failure có kiểu, khô...

Đa ngôn ngữ trong Flutter với file ARB: số nhiều, giống, và những chỗ cắn người

Commit đa ngôn ngữ đầu tiên trong đa số ứng dụng Flutter là một Map<String, String> đánh khoá theo mã ngôn ngữ, tra qua một biến toàn cục. Nó chạy được với hai ngôn ngữ và một trăm chuỗi. Nó vỡ ngay lần đầu ai đó cần “1 item” so với “2 items”, và vỡ nặng ngay lần đầu một người dịch hỏi home_screen_label_2 là để làm gì. Câu trả lời chính thức của Flutter là file ARB được gen_l10n biên dịch thành một class Dart sinh tự động. Phần cài đặt rất ngắn. Thứ đáng hiểu là những phần mà bài quickstart không nhắc: plural theo ICU, kiểu của placeholder, cách phân giải locale, và chuyện gì xảy ra vào ngày có người thêm tiếng Ả Rập. Cài đặt, một lần # pubspec.yaml dependencies : flutter_localizations : sdk : flutter intl : any flutter : generate : true # l10n.yaml ở thư mục gốc dự án arb-dir : lib/l10n template-arb-file : app_en.arb output-localization-file : app_localizations.dart nullable-getter : false nullable-getter: false đáng để đặt một cách có chủ ý. Với nó...

Flutter localization with ARB and gen_l10n that translators like — hướng dẫn Flutter

Ship the l10n pipeline early — retrofitting 200 strings is painful. Bài này chuyển quan sát đó thành một mô hình nhỏ để bạn có thể kiểm thử, đo lường và giữ cho code dễ bảo trì khi app lớn lên. Vấn đề cốt lõi Untranslated messages, plurals, and gender-aware copy. Câu hỏi hữu ích không phải API hay pattern có đẹp riêng lẻ hay không, mà là state nằm ở đâu, boundary nào chịu trách nhiệm khi lỗi xảy ra, và người dùng phục hồi thế nào khi happy path biến mất. Mô hình thực tế ARB + gen_l10n is the supported path; keep keys stable for translators. Hãy bắt đầu bằng một owner rõ ràng cho behavior. Widget chỉ nên render và phát intent; IO, persistence, permission và retry nên nằm sau một interface nhỏ. Nhờ vậy bạn có seam để fake trong test và một chỗ ghi lại các dữ kiện cần theo dõi ở production. Trong app Flutter, boundary thường có dạng: Widget phát intent như load, submit, refresh hoặc retry. Controller hoặc use case validate intent rồi gọi boundary. Boundary trả về data có kiểu h...

RTL layouts that do not look broken in Flutter

Half the world reads right-to-left. Mirroring is not optional. This article turns that observation into a small implementation model you can test, measure, and keep boring when the app grows. The problem in one sentence Directionality, icons, and mirrored gestures. The useful question is not whether the API or pattern looks elegant in isolation. It is where state lives, which boundary owns failure, and how a user recovers when the happy path disappears. A practical model Use start/end, not left/right, in padding and alignment. Start with one explicit owner for the behavior. Keep widgets responsible for rendering and user intent; keep IO, persistence, permissions, and retries behind a small interface. That gives you a seam for a fake in tests and a place to record the facts that matter in production. For a Flutter app, the boundary usually looks like this: The widget emits an intent such as load, submit, refresh, or retry. A controller or use case validates the intent and call...

Pseudolocales and expansion testing before translators

German and Vietnamese expand. Arabic flips. Test before paying translators. This article turns that observation into a small implementation model you can test, measure, and keep boring when the app grows. The problem in one sentence Finding truncation bugs without a full translation set. The useful question is not whether the API or pattern looks elegant in isolation. It is where state lives, which boundary owns failure, and how a user recovers when the happy path disappears. A practical model Pseudolocales catch overflow for free. Start with one explicit owner for the behavior. Keep widgets responsible for rendering and user intent; keep IO, persistence, permissions, and retries behind a small interface. That gives you a seam for a fake in tests and a place to record the facts that matter in production. For a Flutter app, the boundary usually looks like this: The widget emits an intent such as load, submit, refresh, or retry. A controller or use case validates the intent and...

Flutter localization with ARB and gen_l10n that translators like

Ship the l10n pipeline early — retrofitting 200 strings is painful. This article turns that observation into a small implementation model you can test, measure, and keep boring when the app grows. The problem in one sentence Untranslated messages, plurals, and gender-aware copy. The useful question is not whether the API or pattern looks elegant in isolation. It is where state lives, which boundary owns failure, and how a user recovers when the happy path disappears. A practical model ARB + gen_l10n is the supported path; keep keys stable for translators. Start with one explicit owner for the behavior. Keep widgets responsible for rendering and user intent; keep IO, persistence, permissions, and retries behind a small interface. That gives you a seam for a fake in tests and a place to record the facts that matter in production. For a Flutter app, the boundary usually looks like this: The widget emits an intent such as load, submit, refresh, or retry. A controller or use case ...

Đa ngôn ngữ trong Flutter với file ARB: số nhiều, giống, và những chỗ cắn người

Commit đa ngôn ngữ đầu tiên trong đa số ứng dụng Flutter là một Map<String, String> đánh khoá theo mã ngôn ngữ, tra qua một biến toàn cục. Nó chạy được với hai ngôn ngữ và một trăm chuỗi. Nó vỡ ngay lần đầu ai đó cần “1 item” so với “2 items”, và vỡ nặng ngay lần đầu một người dịch hỏi home_screen_label_2 là để làm gì. Câu trả lời chính thức của Flutter là file ARB được gen_l10n biên dịch thành một class Dart sinh tự động. Phần cài đặt rất ngắn. Thứ đáng hiểu là những phần mà bài quickstart không nhắc: plural theo ICU, kiểu của placeholder, cách phân giải locale, và chuyện gì xảy ra vào ngày có người thêm tiếng Ả Rập. Cài đặt, một lần # pubspec.yaml dependencies : flutter_localizations : sdk : flutter intl : any flutter : generate : true # l10n.yaml ở thư mục gốc dự án arb-dir : lib/l10n template-arb-file : app_en.arb output-localization-file : app_localizations.dart nullable-getter : false nullable-getter: false đáng để đặt một cách có chủ ý. Với nó...