{"id":36213747,"url":"https://github.com/moinsen-dev/shard_i18n","last_synced_at":"2026-01-11T04:28:48.419Z","repository":{"id":322434106,"uuid":"1089423982","full_name":"moinsen-dev/shard_i18n","owner":"moinsen-dev","description":"Runtime, sharded, msgid-based i18n for Flutter with no codegen. Supports dynamic language switching, plurals, interpolation, and AI-powered translation CLI.","archived":false,"fork":false,"pushed_at":"2025-12-18T09:56:54.000Z","size":503,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"develop","last_synced_at":"2025-12-21T16:36:43.741Z","etag":null,"topics":["ai","dart","flutter","i18n"],"latest_commit_sha":null,"homepage":"https://moinsen-dev.github.io/shard_i18n/","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/moinsen-dev.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-11-04T10:21:59.000Z","updated_at":"2025-12-18T13:05:00.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/moinsen-dev/shard_i18n","commit_stats":null,"previous_names":["moinsen-dev/shard_i18n"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/moinsen-dev/shard_i18n","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moinsen-dev%2Fshard_i18n","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moinsen-dev%2Fshard_i18n/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moinsen-dev%2Fshard_i18n/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moinsen-dev%2Fshard_i18n/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/moinsen-dev","download_url":"https://codeload.github.com/moinsen-dev/shard_i18n/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moinsen-dev%2Fshard_i18n/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28284027,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-11T03:48:11.750Z","status":"ssl_error","status_checked_at":"2026-01-11T03:48:02.765Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","dart","flutter","i18n"],"created_at":"2026-01-11T04:28:47.647Z","updated_at":"2026-01-11T04:28:48.406Z","avatar_url":"https://github.com/moinsen-dev.png","language":"Dart","funding_links":[],"categories":[],"sub_categories":[],"readme":"# shard_i18n\n\n**Runtime, sharded, msgid-based internationalization for Flutter - no code generation required.**\n\nA tiny, production-ready i18n layer for Flutter that solves the pain points of traditional approaches:\n- ✅ **No codegen** - Pure runtime lookups with `context.t('Sign in')` or `'Sign in'.tx`\n- ✅ **Sharded by feature** - `assets/i18n/\u003clocale\u003e/\u003cfeature\u003e.json` prevents merge conflicts\n- ✅ **Msgid ergonomics** - Use readable English directly in code; auto-fallback if missing\n- ✅ **BLoC-ready** - Tiny `LanguageCubit` drives `Locale`; UI pulls strings via context\n- ✅ **Dynamic switching** - Change language at runtime without restart\n- ✅ **CLDR plurals** - Proper `one/few/many/other` forms for 15+ languages\n- ✅ **Automated migration** - Migrate existing apps automatically with `shard_i18n_migrator`\n- ✅ **AI-powered CLI** - Auto-translate missing keys with OpenAI/DeepL\n- ✅ **Code-to-JSON sync** - `extract` command finds i18n usage and compares with JSON files\n\n[![pub package](https://img.shields.io/pub/v/shard_i18n.svg)](https://pub.dev/packages/shard_i18n)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![CI](https://github.com/moinsen-dev/shard_i18n/workflows/CI/badge.svg)](https://github.com/moinsen-dev/shard_i18n/actions)\n\n📚 **[Full Documentation](https://moinsen-dev.github.io/shard_i18n/)** | 📦 **[pub.dev](https://pub.dev/packages/shard_i18n)** | 🐛 **[Issues](https://github.com/moinsen-dev/shard_i18n/issues)**\n\n---\n\n## Why shard_i18n?\n\nLarge teams fight over one giant ARB/JSON file and slow codegen cycles. `shard_i18n` removes those bottlenecks:\n\n| Problem | shard_i18n Solution |\n|---------|---------------------|\n| Merge conflicts in monolithic translation files | **Sharded** translations by feature (`core.json`, `auth.json`, etc.) |\n| Slow code generation cycles | **No codegen** - direct runtime lookups |\n| Cryptic generated method names | **Natural msgid** usage: `context.t('Sign in')` |\n| Complex setup for dynamic language switching | Built-in **locale switching** with `AnimatedBuilder` |\n| Manual plural form management | **CLDR-based** plural resolver for 15+ languages |\n| Time-consuming manual migration | **Automated migrator** extracts and transforms strings automatically |\n| Tedious translation workflows | **AI-powered CLI** to fill missing translations |\n\n---\n\n## Installation\n\n### 1. Add dependency\n\n```yaml\n# pubspec.yaml\ndependencies:\n  flutter:\n    sdk: flutter\n  shard_i18n: ^0.3.0\n  flutter_bloc: ^8.1.4        # for state management (optional but recommended)\n  shared_preferences: ^2.2.3   # for persisting language choice (optional)\n\nflutter:\n  assets:\n    - assets/i18n/\n```\n\n### 2. Create translation assets\n\nCreate sharded JSON files per locale:\n\n```\nassets/i18n/\n  en/\n    core.json\n    auth.json\n  de/\n    core.json\n    auth.json\n  tr/\n    core.json\n  ru/\n    core.json\n```\n\n**Example:** `assets/i18n/en/auth.json`\n\n```json\n{\n  \"Sign in\": \"Sign in\",\n  \"Hello, {name}!\": \"Hello, {name}!\",\n  \"items_count\": {\n    \"one\": \"{count} item\",\n    \"other\": \"{count} items\"\n  }\n}\n```\n\n**German:** `assets/i18n/de/auth.json`\n\n```json\n{\n  \"Sign in\": \"Anmelden\",\n  \"Hello, {name}!\": \"Hallo, {name}!\",\n  \"items_count\": {\n    \"one\": \"{count} Artikel\",\n    \"other\": \"{count} Artikel\"\n  }\n}\n```\n\n---\n\n## Quick Start\n\n### 1. Bootstrap in `main()`\n\n```dart\nimport 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:flutter_localizations/flutter_localizations.dart';\nimport 'package:shard_i18n/shard_i18n.dart';\nimport 'language_cubit.dart'; // see below\n\nvoid main() async {\n  WidgetsFlutterBinding.ensureInitialized();\n\n  final initialLocale = await LanguageCubit.loadInitial();\n  await ShardI18n.instance.bootstrap(initialLocale);\n\n  runApp(\n    BlocProvider(\n      create: (_) =\u003e LanguageCubit(initialLocale),\n      child: const MyApp(),\n    ),\n  );\n}\n```\n\n### 2. Wire up MaterialApp\n\n```dart\nclass MyApp extends StatelessWidget {\n  const MyApp({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    final locale = context.watch\u003cLanguageCubit\u003e().state;\n\n    return AnimatedBuilder(\n      animation: ShardI18n.instance,\n      builder: (_, __) {\n        return MaterialApp(\n          locale: locale,\n          localizationsDelegates: const [\n            GlobalMaterialLocalizations.delegate,\n            GlobalWidgetsLocalizations.delegate,\n            GlobalCupertinoLocalizations.delegate,\n          ],\n          supportedLocales: ShardI18n.instance.supportedLocales,\n          home: const HomePage(),\n        );\n      },\n    );\n  }\n}\n```\n\n### 3. Use in widgets\n\n```dart\nclass HomePage extends StatelessWidget {\n  const HomePage({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        title: Text(context.t('Hello, {name}!', params: {'name': 'World'})),\n      ),\n      body: Center(\n        child: Text(context.tn('items_count', count: 5)),\n      ),\n      floatingActionButton: FloatingActionButton(\n        onPressed: () =\u003e context.read\u003cLanguageCubit\u003e().setLocale(Locale('de')),\n        child: const Icon(Icons.language),\n      ),\n    );\n  }\n}\n```\n\n---\n\n## API Reference\n\n### BuildContext Extensions\n\n```dart\n// Simple translation with interpolation\ncontext.t('Hello, {name}!', params: {'name': 'Alice'})\n\n// Plural forms (automatically selects one/few/many/other based on locale)\ncontext.tn('items_count', count: 5)\n```\n\n### String Extensions\n\nFor even more concise code, use string extensions (no `context` required):\n\n```dart\n// Simple translation (getter)\nText('Hello World'.tx)\n\n// Translation with parameters\nText('Hello, {name}!'.t({'name': 'Alice'}))\n\n// Plural forms\nText('items_count'.tn(count: 5))\n```\n\n| Method | Description | Example |\n|--------|-------------|---------|\n| `.tx` | Simple translation (getter) | `'Sign in'.tx` |\n| `.t()` | Translation with optional params | `'Hello, {name}!'.t({'name': 'World'})` |\n| `.tn()` | Pluralization with count | `'items_count'.tn(count: 5)` |\n\n\u003e **Note:** String extensions use `ShardI18n.instance` directly, so they work anywhere - in widgets, controllers, or utility classes.\n\n### ShardI18n Singleton\n\n```dart\n// Bootstrap before runApp\nawait ShardI18n.instance.bootstrap(Locale('en'));\n\n// Change locale at runtime\nawait ShardI18n.instance.setLocale(Locale('de'));\n\n// Get current locale\nShardI18n.instance.locale\n\n// Get discovered locales from assets\nShardI18n.instance.supportedLocales\n\n// Register custom plural rules\nShardI18n.instance.registerPluralRule('fr', (n) =\u003e n \u003c= 1 ? 'one' : 'other');\n\n// Clear cache (useful for testing)\nShardI18n.instance.clearCache();\n```\n\n### LanguageCubit (Example)\n\n```dart\nimport 'package:flutter/widgets.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';\nimport 'package:shared_preferences/shared_preferences.dart';\nimport 'package:shard_i18n/shard_i18n.dart';\n\nclass LanguageCubit extends Cubit\u003cLocale\u003e {\n  LanguageCubit(super.initialLocale);\n\n  static const _k = 'app_locale';\n\n  static Future\u003cLocale\u003e loadInitial() async {\n    final prefs = await SharedPreferences.getInstance();\n    final saved = prefs.getString(_k);\n    if (saved != null \u0026\u0026 saved.isNotEmpty) {\n      final p = saved.split('-');\n      return p.length == 2 ? Locale(p[0], p[1]) : Locale(p[0]);\n    }\n    return WidgetsBinding.instance.platformDispatcher.locale;\n  }\n\n  Future\u003cvoid\u003e setLocale(Locale locale) async {\n    if (state == locale) return;\n    await ShardI18n.instance.setLocale(locale);\n    final prefs = await SharedPreferences.getInstance();\n    final tag = locale.countryCode?.isNotEmpty == true\n        ? '${locale.languageCode}-${locale.countryCode}'\n        : locale.languageCode;\n    await prefs.setString(_k, tag);\n    emit(locale);\n  }\n}\n```\n\n---\n\n## Features\n\n### 1. Msgid vs. Stable IDs\n\nBy default, use **natural English msgids** for readability:\n\n```dart\nText(context.t('Sign in'))\n```\n\nSwitch to **stable IDs** when English copy is volatile:\n\n```dart\nText(context.t('auth.sign_in'))\n```\n\nThe lookup works for both! English translation file becomes:\n\n```json\n{\n  \"auth.sign_in\": \"Sign in\"\n}\n```\n\n### 2. Interpolation\n\nNamed placeholders using `{name}` syntax:\n\n```json\n{\n  \"Hello, {name}!\": \"Hallo, {name}!\"\n}\n```\n\n```dart\ncontext.t('Hello, {name}!', params: {'name': 'Uli'})\n```\n\n### 3. Plurals (CLDR-style)\n\nDefine plural forms matching your locale's rules:\n\n```json\n{\n  \"items_count\": {\n    \"one\": \"{count} item\",\n    \"other\": \"{count} items\"\n  }\n}\n```\n\n**Russian** (complex `one/few/many/other`):\n\n```json\n{\n  \"items_count\": {\n    \"one\": \"{count} предмет\",\n    \"few\": \"{count} предмета\",\n    \"many\": \"{count} предметов\",\n    \"other\": \"{count} предмета\"\n  }\n}\n```\n\n**Turkish** (no plural distinction):\n\n```json\n{\n  \"items_count\": {\n    \"other\": \"{count} öğe\"\n  }\n}\n```\n\nSupported plural rules: `en`, `de`, `nl`, `sv`, `no`, `da`, `fi`, `it`, `es`, `pt`, `tr`, `ro`, `bg`, `el`, `hu`, `ru`, `uk`, `sr`, `hr`, `bs`, `pl`, `cs`, `sk`, `fr`, `lt`, `lv`.\n\n### 4. Fallback Strategy\n\n```\n1. Locale + country (e.g., de-DE)\n   ↓ (if missing)\n2. Locale language (e.g., de)\n   ↓ (if missing)\n3. English (en)\n   ↓ (if missing)\n4. Msgid/stable ID itself (developer-friendly)\n```\n\n### 5. Dynamic Locale Switching\n\n```dart\nawait context.read\u003cLanguageCubit\u003e().setLocale(Locale('de'));\n```\n\nShardI18n hot-loads the new locale's shards and notifies `AnimatedBuilder` to rebuild the UI. No app restart required!\n\n---\n\n## CLI Tools\n\nshard_i18n includes two powerful CLI tools to streamline your i18n workflow:\n\n### Installation\n\n**Global installation (recommended):**\n\n```bash\n# Install globally\ndart pub global activate shard_i18n\n\n# Use commands directly\nshard_i18n_cli verify\nshard_i18n_migrator analyze lib/\n```\n\n**Local execution (without global install):**\n\n```bash\n# Run from your project directory\ndart run shard_i18n_cli verify\ndart run shard_i18n_migrator analyze lib/\n```\n\n---\n\n## 1. Translation Management CLI (`shard_i18n_cli`)\n\nManage and automate translation workflows.\n\n### Verify Translations\n\nCheck for missing keys and placeholder consistency:\n\n```bash\nshard_i18n_cli verify\n# or: dart run shard_i18n_cli verify\n```\n\nOutput:\n\n```\n🔍 Verifying translations in: assets/i18n\n\n📁 Found locales: en, de, tr, ru\n\n📊 Reference locale: en (15 keys)\n\n  de:\n    ✅ All keys present (15 keys)\n\n  tr:\n    ⚠️  Missing 2 key(s):\n       - Welcome to shard_i18n\n       - Features\n\n  ru:\n    ✅ All keys present (15 keys)\n```\n\n### Fill Missing Translations\n\nAuto-translate missing keys using AI:\n\n```bash\n# Using OpenAI\nshard_i18n_cli fill \\\n  --from=en \\\n  --to=de,tr,fr \\\n  --provider=openai \\\n  --key=$OPENAI_API_KEY\n\n# Using DeepL\nshard_i18n_cli fill \\\n  --from=en \\\n  --to=de \\\n  --provider=deepl \\\n  --key=$DEEPL_API_KEY\n\n# Dry run (preview without writing)\nshard_i18n_cli fill \\\n  --from=en \\\n  --to=de \\\n  --provider=openai \\\n  --key=$OPENAI_API_KEY \\\n  --dry-run\n```\n\nThe CLI preserves `{placeholders}` and writes translated entries to the appropriate locale files.\n\n### Extract i18n Keys from Code (NEW in v0.3.0)\n\nScan your source code to find all i18n usage and compare with JSON files:\n\n```bash\n# Basic usage - find discrepancies\nshard_i18n_cli extract\n\n# JSON output for CI/CD\nshard_i18n_cli extract --format=json --strict\n\n# Auto-fix missing keys\nshard_i18n_cli extract --fix\n\n# Remove orphaned keys\nshard_i18n_cli extract --prune\n\n# Preview changes\nshard_i18n_cli extract --fix --dry-run\n```\n\n**Features:**\n- Detects all i18n patterns: `context.t()`, `context.tn()`, `'key'.tx`, `'key'.t()`, `'key'.tn()`\n- Three output formats: `text`, `json`, `diff`\n- `--strict` mode for CI (exit code 1 on issues)\n- `--fix` auto-generates missing entries\n- `--prune` removes orphaned keys from JSON\n- Validates placeholder consistency\n- Checks plural form structure\n\n**Example output:**\n```\n🔍 Scanning lib/ for i18n usage...\n\n✅ Statistics:\n   Files scanned:     42\n   Keys in code:      156\n   Keys in JSON:      160\n   Matched:           152 (97.4%)\n   Missing in JSON:   4\n   Orphaned in JSON:  8\n\n❌ Missing in JSON (4):\n   • \"New feature text\"\n   • \"Upload failed: {error}\"\n```\n\n---\n\n## 2. Migration Tool (`shard_i18n_migrator`)\n\nAutomatically migrate existing Flutter apps to use shard_i18n. The migrator analyzes your codebase, extracts translatable strings, and transforms your code to use the shard_i18n API.\n\n### Analyze Your Project\n\nPreview what strings will be extracted without making changes:\n\n```bash\nshard_i18n_migrator analyze lib/\n# or: dart run shard_i18n_migrator analyze lib/\n```\n\nOutput shows:\n- Total translatable strings found\n- Breakdown by category (extractable, technical, ambiguous)\n- Confidence scores\n- Strings with interpolation and plurals\n\n**Options:**\n- `--verbose` - Show detailed analysis per file\n- `--config=path/to/config.yaml` - Use custom configuration\n\n### Migrate Your Project\n\nTransform your code to use shard_i18n:\n\n```bash\n# Dry run (preview changes without writing)\nshard_i18n_migrator migrate lib/ --dry-run\n\n# Interactive mode (asks for confirmation on ambiguous strings)\nshard_i18n_migrator migrate lib/\n\n# Automatic mode (extracts everything above confidence threshold)\nshard_i18n_migrator migrate lib/ --auto\n```\n\n**What it does:**\n1. **Analyzes** all Dart files in the specified directory\n2. **Extracts** translatable strings (UI text, error messages, etc.)\n3. **Transforms** code to use `context.t()` and `context.tn()` for plurals\n4. **Generates** JSON translation files in `assets/i18n/en/`\n5. **Adds** necessary imports (`package:shard_i18n/shard_i18n.dart`)\n6. **Preserves** interpolation parameters and plural forms\n\n**Migration options:**\n- `--dry-run` - Preview without modifying files\n- `--auto` - Skip interactive prompts for ambiguous strings\n- `--verbose` - Show detailed migration progress\n- `--config=path` - Use custom migration config\n- `--threshold=0.8` - Set confidence threshold (0.0-1.0)\n\n### Configuration\n\nCreate a `shard_i18n_config.yaml` for fine-tuned migration:\n\n```yaml\n# Minimum confidence score to auto-extract (0.0 - 1.0)\nautoExtractThreshold: 0.7\n\n# Directories to exclude from analysis\nexcludePaths:\n  - lib/generated/\n  - test/\n  - .dart_tool/\n\n# Patterns to skip (regex)\nskipPatterns:\n  - '^[A-Z_]+$'  # ALL_CAPS constants\n  - '^\\d+$'      # Pure numbers\n\n# Feature-based sharding\nsharding:\n  enabled: true\n  defaultFeature: core\n  # Map directories to features\n  featureMapping:\n    lib/auth/: auth\n    lib/settings/: settings\n    lib/profile/: profile\n```\n\n### Migration Workflow\n\n**Recommended workflow for existing apps:**\n\n1. **Analyze first:**\n   ```bash\n   shard_i18n_migrator analyze lib/ --verbose\n   ```\n\n2. **Test on a single feature:**\n   ```bash\n   shard_i18n_migrator migrate lib/auth/ --dry-run\n   shard_i18n_migrator migrate lib/auth/\n   ```\n\n3. **Run tests:**\n   ```bash\n   flutter test\n   ```\n\n4. **Migrate remaining code:**\n   ```bash\n   shard_i18n_migrator migrate lib/ --auto\n   ```\n\n5. **Verify translations:**\n   ```bash\n   shard_i18n_cli verify\n   ```\n\n**Interactive mode** is recommended for the first migration - it prompts for confirmation on strings with low confidence scores, helping you avoid extracting technical strings or constants.\n\n---\n\n## Folder Structure (Best Practices)\n\n```\nassets/i18n/\n  en/              # Source locale\n    core.json      # App-wide strings\n    auth.json      # Authentication feature\n    settings.json  # Settings feature\n  de/              # German translations\n    core.json\n    auth.json\n    settings.json\n  tr/              # Turkish translations\n    core.json\n    auth.json\n  ru/              # Russian translations\n    core.json\n```\n\n**Why sharded?**\n- **Fewer merge conflicts**: Feature teams work on separate files\n- **Faster loading**: Only current locale loaded (not all languages)\n- **Easier maintenance**: Clear ownership per feature\n\n---\n\n## Testing\n\n### Unit Tests\n\n```dart\nimport 'package:flutter_test/flutter_test.dart';\nimport 'package:shard_i18n/shard_i18n.dart';\n\nvoid main() {\n  test('interpolation works', () {\n    final result = ShardI18n.instance.translate(\n      'Hello, {name}!',\n      params: {'name': 'World'},\n    );\n    expect(result, equals('Hello, World!'));\n  });\n}\n```\n\n### Widget Tests\n\n```dart\ntestWidgets('displays translated text', (tester) async {\n  await ShardI18n.instance.bootstrap(Locale('de'));\n  await tester.pumpWidget(MyApp());\n  expect(find.text('Anmelden'), findsOneWidget); // German \"Sign in\"\n});\n```\n\n---\n\n## Migration Guide\n\n### Automated Migration (Recommended)\n\nUse the **shard_i18n_migrator** tool for automated migration from any existing i18n solution:\n\n```bash\n# 1. Analyze your codebase\nshard_i18n_migrator analyze lib/ --verbose\n\n# 2. Run migration (interactive mode)\nshard_i18n_migrator migrate lib/\n\n# 3. Review changes and test\nflutter test\n\n# 4. Verify translations\nshard_i18n_cli verify\n```\n\nThe migrator automatically:\n- ✅ Extracts translatable strings from your code\n- ✅ Transforms to `context.t()` and `context.tn()` calls\n- ✅ Generates JSON translation files\n- ✅ Preserves interpolation and plural forms\n- ✅ Adds necessary imports\n\nSee the [Migration Tool section](#2-migration-tool-shard_i18n_migrator) above for detailed usage.\n\n### Manual Migration\n\nIf you prefer manual migration or have a unique setup:\n\n1. Export your current locale files to `assets/i18n/\u003clocale\u003e/core.json`\n2. Replace generated method calls with `context.t('msgid')`\n3. Keep `GlobalMaterialLocalizations` etc. if you use them\n4. Run `shard_i18n_cli verify` to check consistency\n\n**Mixed mode** is fine: Keep legacy screens on old i18n while moving new features to `shard_i18n`.\n\n---\n\n## Performance\n\n- **Startup**: Only current locale shards loaded (lazy, async)\n- **Locale switch**: ~50-100ms for typical app (depends on shard count)\n- **Lookups**: O(1) HashMap lookups in memory\n- **Interpolation**: Simple regex replace\n- **Best practice**: Keep shards \u003c5-10k lines each\n\n---\n\n## Roadmap\n\n- [x] ~~Build-time reporting for CI (missing keys diff)~~ - Added in v0.3.0 with `extract` command\n- [ ] Rich ICU message format support (`select`, `gender`)\n- [ ] Dev overlay for live-editing translations in debug mode\n- [ ] VS Code extension (quick-add keys, jump to definition)\n- [ ] JSON schema validation for translation files\n\n---\n\n## Contributing\n\nContributions welcome! Please:\n\n1. Open an issue first to discuss major changes\n2. Add tests for new features\n3. Run `flutter test` before submitting PR\n4. Follow existing code style\n\n### For Maintainers\n\nThis package uses automated publishing via GitHub Actions. See [PUBLISHING.md](PUBLISHING.md) for details on releasing new versions.\n\n**Quick release process:**\n1. Update version in `pubspec.yaml` and `CHANGELOG.md`\n2. Run `./scripts/pre_publish_check.sh` to verify readiness\n3. Create and push a version tag: `git tag -a v0.2.0 -m \"Release 0.2.0\" \u0026\u0026 git push origin v0.2.0`\n4. GitHub Actions will automatically publish to pub.dev\n\n---\n\n## License\n\nMIT License - see [LICENSE](LICENSE) file for details.\n\n---\n\n## Support\n\n- **Issues**: [GitHub Issues](https://github.com/moinsen-dev/shard_i18n/issues)\n- **Discussions**: [GitHub Discussions](https://github.com/moinsen-dev/shard_i18n/discussions)\n- **Email**: support@moinsen.dev\n\n---\n\n**Made with ❤️ by the moinsen team**\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoinsen-dev%2Fshard_i18n","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmoinsen-dev%2Fshard_i18n","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoinsen-dev%2Fshard_i18n/lists"}