stringlocale 0.2.0
stringlocale: ^0.2.0 copied to clipboard
Compile-time LLM localization for Dart and Flutter. Define strings once with typed params, generate locale JSON at build time, translate dynamic values at runtime via OpenRouter.
example/main.dart
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:stringlocale/stringlocale_flutter.dart';
import 'texts.dart';
// In a real app you'd load these from compiled JSON assets.
// Shown inline here for a self-contained example.
final Map<String, Map<String, dynamic>> localeData = {
'ne-NP': jsonDecode(_neNP) as Map<String, dynamic>,
'ja-JP': jsonDecode(_jaJP) as Map<String, dynamic>,
};
void main() {
final renderer = Renderer(
localeData: localeData,
// apiKey is read from OPENROUTER_API_KEY for translatable/userAdapted params
);
runApp(MyApp(renderer: renderer));
}
class MyApp extends StatefulWidget {
const MyApp({super.key, required this.renderer});
final Renderer renderer;
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String _locale = 'en';
String _language = 'English';
@override
Widget build(BuildContext context) {
return MaterialApp(
home: StringLocaleScope(
localeCode: _locale,
languageName: _language,
renderer: widget.renderer,
child: Builder(
builder: (context) => Scaffold(
appBar: AppBar(title: Tr(welcome)),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Locale switcher
Wrap(
spacing: 8,
children: [
_localeButton(context, 'en', 'English'),
_localeButton(context, 'ne-NP', 'Nepali'),
_localeButton(context, 'ja-JP', 'Japanese'),
],
),
const SizedBox(height: 24),
// Plural
Tr(pageCount, args: {'count': 5}),
const SizedBox(height: 8),
// Currency (NPR, digit-converted)
Tr(websitePlanPrice,
args: {'owner': 'Jane Doe', 'amount': 2500}),
const SizedBox(height: 8),
// Translatable status — LLM at runtime
Tr(websiteStatus, args: {'status': 'approved'}),
const SizedBox(height: 8),
// Date param
Tr(launchDate, args: {'date': '2025-02-15'}),
],
),
),
),
),
),
);
}
Widget _localeButton(BuildContext context, String code, String name) {
final selected = _locale == code;
return ElevatedButton(
onPressed: () {
setState(() {
_locale = code;
_language = name;
});
},
style: ElevatedButton.styleFrom(
backgroundColor: selected ? Colors.indigo : null,
foregroundColor: selected ? Colors.white : null,
),
child: Text(name),
);
}
}
// Compiled locale fixtures (normally generated by `dart run example/compile.dart`)
const String _neNP = '''
{
"welcome": {"text": "stringlocale मा स्वागत छ", "src_hash": "a1"},
"website_plan_price": {"text": "{owner} को वेबसाइट योजनाको लागत प्रति महिना {amount} छ", "src_hash": "a2"},
"website_status": {"text": "वेबसाइट स्थिति: {status}", "src_hash": "a3"},
"launch_date": {"text": "{date} सम्ममा सुरु गर्नुहोस्", "src_hash": "a4"},
"page_count": {
"singular": "तपाईंसँग {count} स्थानीयकृत पृष्ठ छ",
"plural": "तपाईंसँग {count} स्थानीयकृत पृष्ठहरू छन्",
"rule": "count < 2",
"src_hash": "a5"
}
}
''';
const String _jaJP = '''
{
"welcome": {"text": "stringlocaleへようこそ", "src_hash": "a1"},
"website_plan_price": {"text": "{owner}のウェブサイトプランは月額{amount}です", "src_hash": "a2"},
"website_status": {"text": "ウェブサイトのステータス: {status}", "src_hash": "a3"},
"launch_date": {"text": "{date}までに開始", "src_hash": "a4"},
"page_count": {
"singular": "{count} 個のローカライズされたページがあります",
"plural": "{count} 個のローカライズされたページがあります",
"rule": "false",
"src_hash": "a5"
}
}
''';