feat(i18n): full RTL/CJK support — fonts, Japanese locale, directional fixes

Bundle Noto Sans Arabic + Noto Sans JP (SIL OFL 1.1) as per-glyph fallbacks
for both the UI text theme and every generated PDF, so RTL (Arabic) and CJK
render on all platforms — including desktop, where the system font may lack
them — instead of tofu. A shared pdf_fonts.dart centralizes the PDF theme and
the three label/catalog/recovery services reuse it.

Add Japanese (ja) as the CJK reference locale — fittingly, the app's own name
is Japanese (種, tane, 'seed'). Core strings translated; the rest fall back to
English via slang. Wired into supportedLocales (automatic) and the settings
picker.

Fix three physical Alignment.centerLeft -> AlignmentDirectional.centerStart in
variety_detail_screen, and make month-abbreviation and avatar-initial
truncation grapheme-safe (.characters) so CJK text is never cut mid-character.

Tests: CJK/Arabic PDF glyph render, ja locale resolution + English fallback.
The chat-bubble mirroring flagged by the audit was verified correct (already
respects Directionality) and left unchanged.
This commit is contained in:
vjrj 2026-07-14 11:11:19 +02:00
parent 41ea6735d2
commit baa8867287
31 changed files with 686 additions and 104 deletions

View file

@ -3,10 +3,10 @@
/// Source: lib/i18n
/// To regenerate, run: `dart run slang`
///
/// Locales: 6
/// Strings: 3356 (559 per locale)
/// Locales: 7
/// Strings: 3396 (485 per locale)
///
/// Built on 2026-07-13 at 16:11 UTC
/// Built on 2026-07-14 at 09:00 UTC
// coverage:ignore-file
// ignore_for_file: type=lint, unused_import
@ -22,6 +22,7 @@ import 'strings_ast.g.dart' as l_ast;
import 'strings_de.g.dart' as l_de;
import 'strings_es.g.dart' as l_es;
import 'strings_fr.g.dart' as l_fr;
import 'strings_ja.g.dart' as l_ja;
import 'strings_pt.g.dart' as l_pt;
part 'strings_en.g.dart';
@ -37,6 +38,7 @@ enum AppLocale with BaseAppLocale<AppLocale, Translations> {
de(languageCode: 'de'),
es(languageCode: 'es'),
fr(languageCode: 'fr'),
ja(languageCode: 'ja'),
pt(languageCode: 'pt');
const AppLocale({
@ -99,6 +101,12 @@ enum AppLocale with BaseAppLocale<AppLocale, Translations> {
cardinalResolver: cardinalResolver,
ordinalResolver: ordinalResolver,
);
case AppLocale.ja:
return l_ja.TranslationsJa(
overrides: overrides,
cardinalResolver: cardinalResolver,
ordinalResolver: ordinalResolver,
);
case AppLocale.pt:
return l_pt.TranslationsPt(
overrides: overrides,