smart_localization 0.0.3 copy "smart_localization: ^0.0.3" to clipboard
smart_localization: ^0.0.3 copied to clipboard

A backend-agnostic smart localization system for Flutter. Version-controlled translations with intelligent caching, fallback chains, and parameterized translations.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:smart_localization/smart_localization.dart';

// --- MOCK BACKEND IMPLEMENTATION ---
class MockBackend implements TranslationBackend {
  int _version = 1;
  final Map<String, Map<String, String>> _database = {
    'en': {
      'home.title': 'Smart Localization',
      'home.greeting': 'Hello, User!',
    },
    'tr': {
      'home.title': 'Akıllı Yerelleştirme',
      'home.greeting': 'Merhaba, Kullanıcı!',
    },
  };

  @override
  Future<TranslationData?> fetchTranslations(String languageCode) async {
    final translations = _database[languageCode];
    if (translations == null) return null;
    return TranslationData(
      translations: Map.from(translations),
      version: _version,
    );
  }

  @override
  Future<int> getRemoteVersion() async {
    return _version;
  }

  @override
  Future<List<String>> getSupportedLanguages() async => _database.keys.toList();

  void updateTranslation(String lang, String key, String value) {
    if (_database[lang] != null) _database[lang]![key] = value;
  }

  void publishChanges() {
    _version++;
  }

  Map<String, String> getRawData(String lang) => _database[lang] ?? {};
}

final mockBackend = MockBackend();

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await SmartLocalization.initialize(
    backend: mockBackend,
    config: const SmartLocalizationConfig(
      defaultLanguage: 'en',
      enableLogging: true,
    ),
  );
  runApp(const MyApp());
}

// SpeakBlend Authentic Colors & Typography
class SpeakblendColors {
  static const Color backgroundColor = Color.fromARGB(255, 5, 11, 11);
  static const Color blackColor = Color.fromARGB(255, 5, 11, 11);
  static const Color whiteColor = Colors.white;
  static const Color whiteTextColor = Color(0XFFE0E0E0);
  static const Color borderColor = Color(0XFF2F2F2F);
  static const Color searchBarColor = Color.fromARGB(255, 24, 24, 27);
  static const Color primaryColor = Color.fromARGB(255, 255, 36, 36);
  static const Color grayColor = Color.fromARGB(255, 58, 61, 64);
  static const Color darkGrayTextColor = Color.fromARGB(255, 103, 109, 114);
}

class SpeakblendTheme {
  static TextStyle regularText({required Color color, double? fontSize}) =>
      TextStyle(
          fontFamily: 'Inter',
          color: color,
          fontSize: fontSize,
          fontWeight: FontWeight.w400,
          letterSpacing: -0.3);
  static TextStyle mediumText({required Color color, double? fontSize}) =>
      TextStyle(
          fontFamily: 'Inter',
          color: color,
          fontSize: fontSize,
          fontWeight: FontWeight.w500,
          letterSpacing: -0.3);
  static TextStyle semiBoldText({required Color color, double? fontSize}) =>
      TextStyle(
          fontFamily: 'Inter',
          color: color,
          fontSize: fontSize,
          fontWeight: FontWeight.w600,
          letterSpacing: -0.4);
  static TextStyle boldText({required Color color, double? fontSize}) =>
      TextStyle(
          fontFamily: 'Inter',
          color: color,
          fontSize: fontSize,
          fontWeight: FontWeight.w700,
          letterSpacing: -0.5);
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Smart Localization Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        brightness: Brightness.dark,
        scaffoldBackgroundColor: SpeakblendColors.backgroundColor,
        colorSchemeSeed: SpeakblendColors.primaryColor,
        useMaterial3: true,
        fontFamily: 'Inter',
      ),
      home: const DemoScreen(),
    );
  }
}

class DemoScreen extends StatefulWidget {
  const DemoScreen({super.key});

  @override
  State<DemoScreen> createState() => _DemoScreenState();
}

class _DemoScreenState extends State<DemoScreen> {
  final _languages = ['en', 'tr'];
  int _langIndex = 0;
  bool _isCheckingUpdates = false;

  // Backend Controller
  final TextEditingController _titleController = TextEditingController();
  final TextEditingController _greetingController = TextEditingController();

  @override
  void initState() {
    super.initState();
    _loadBackendData();
  }

  void _loadBackendData() {
    final lang = _languages[_langIndex];
    final data = mockBackend.getRawData(lang);
    _titleController.text = data['home.title'] ?? '';
    _greetingController.text = data['home.greeting'] ?? '';
  }

  Future<void> _switchLanguage() async {
    _langIndex = (_langIndex + 1) % _languages.length;
    await SmartLocalization.instance.setLanguage(_languages[_langIndex]);
    _loadBackendData();
    setState(() {});
  }

  Future<void> _publishAndSync() async {
    setState(() => _isCheckingUpdates = true);

    // 1. Save to Backend
    final lang = _languages[_langIndex];
    mockBackend.updateTranslation(lang, 'home.title', _titleController.text);
    mockBackend.updateTranslation(
        lang, 'home.greeting', _greetingController.text);
    mockBackend.publishChanges();

    // 2. Client checks for updates instantly (bypass throttle)
    await SmartLocalization.instance.refreshTranslations();

    if (mounted) setState(() => _isCheckingUpdates = false);
  }

  @override
  Widget build(BuildContext context) {
    final langCode = SmartLocalization.instance.currentLanguage;
    final langName = SmartLocalization.instance.getLanguageName(langCode);

    return Scaffold(
      body: Center(
        child: SingleChildScrollView(
          padding: const EdgeInsets.symmetric(vertical: 40, horizontal: 24),
          child: Container(
            constraints: const BoxConstraints(maxWidth: 400),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: [
                // ==========================
                // 1. FRONTEND APP UI
                // ==========================
                Text('User App View',
                    style: SpeakblendTheme.boldText(
                        color: SpeakblendColors.darkGrayTextColor,
                        fontSize: 14)),
                const SizedBox(height: 12),
                Container(
                  padding: const EdgeInsets.all(24),
                  decoration: BoxDecoration(
                    color: SpeakblendColors.searchBarColor,
                    borderRadius: BorderRadius.circular(24),
                    border: Border.all(color: SpeakblendColors.borderColor),
                  ),
                  child: Column(
                    children: [
                      // Logo
                      Container(
                        width: 60,
                        height: 60,
                        decoration: BoxDecoration(
                          shape: BoxShape.circle,
                          color: SpeakblendColors.backgroundColor,
                          border:
                              Border.all(color: SpeakblendColors.borderColor),
                        ),
                        child: const Icon(Icons.translate_rounded,
                            color: SpeakblendColors.whiteColor),
                      ),
                      const SizedBox(height: 24),

                      // Translations
                      Text(
                        context.localization('Smart Localization',
                            id: 'home.title'),
                        textAlign: TextAlign.center,
                        style: SpeakblendTheme.boldText(
                            color: SpeakblendColors.whiteColor, fontSize: 24),
                      ),
                      const SizedBox(height: 8),
                      Text(
                        context.localization('Hello, User!',
                            id: 'home.greeting'),
                        textAlign: TextAlign.center,
                        style: SpeakblendTheme.mediumText(
                            color: SpeakblendColors.whiteTextColor,
                            fontSize: 16),
                      ),
                      const SizedBox(height: 24),

                      // Language Switcher Toggle
                      GestureDetector(
                        onTap: _switchLanguage,
                        child: Container(
                          padding: const EdgeInsets.symmetric(
                              horizontal: 16, vertical: 8),
                          decoration: BoxDecoration(
                            color: SpeakblendColors.borderColor,
                            borderRadius: BorderRadius.circular(20),
                          ),
                          child: Row(
                            mainAxisSize: MainAxisSize.min,
                            children: [
                              const Icon(Icons.language_rounded,
                                  size: 16, color: SpeakblendColors.whiteColor),
                              const SizedBox(width: 8),
                              Text(langName.toUpperCase(),
                                  style: SpeakblendTheme.mediumText(
                                      color: SpeakblendColors.whiteColor,
                                      fontSize: 12)),
                            ],
                          ),
                        ),
                      ),
                    ],
                  ),
                ),

                const SizedBox(height: 48),

                // ==========================
                // 2. BACKEND ADMIN UI
                // ==========================
                Text('Backend Database ($langCode)',
                    style: SpeakblendTheme.boldText(
                        color: SpeakblendColors.darkGrayTextColor,
                        fontSize: 14)),
                const SizedBox(height: 12),
                Container(
                  padding: const EdgeInsets.all(24),
                  decoration: BoxDecoration(
                    color: SpeakblendColors.backgroundColor,
                    borderRadius: BorderRadius.circular(24),
                    border: Border.all(color: SpeakblendColors.borderColor),
                  ),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.stretch,
                    children: [
                      // Field 1
                      Text('home.title',
                          style: SpeakblendTheme.mediumText(
                              color: SpeakblendColors.darkGrayTextColor,
                              fontSize: 12)),
                      const SizedBox(height: 8),
                      _buildTextField(_titleController),
                      const SizedBox(height: 16),

                      // Field 2
                      Text('home.greeting',
                          style: SpeakblendTheme.mediumText(
                              color: SpeakblendColors.darkGrayTextColor,
                              fontSize: 12)),
                      const SizedBox(height: 8),
                      _buildTextField(_greetingController),
                      const SizedBox(height: 24),

                      // Publish Button
                      _SpeakBlendButton(
                        text: _isCheckingUpdates
                            ? 'Syncing...'
                            : 'Update & Sync App',
                        icon: Icons.sync_rounded,
                        backgroundColor: SpeakblendColors.whiteColor,
                        textColor: SpeakblendColors.blackColor,
                        isLoading: _isCheckingUpdates,
                        onTap: _publishAndSync,
                      ),
                    ],
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }

  Widget _buildTextField(TextEditingController controller) {
    return TextField(
      controller: controller,
      style: SpeakblendTheme.regularText(
          color: SpeakblendColors.whiteColor, fontSize: 16),
      decoration: InputDecoration(
        filled: true,
        fillColor: SpeakblendColors.searchBarColor,
        contentPadding:
            const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
        enabledBorder: OutlineInputBorder(
            borderRadius: BorderRadius.circular(12),
            borderSide: const BorderSide(color: SpeakblendColors.borderColor)),
        focusedBorder: OutlineInputBorder(
            borderRadius: BorderRadius.circular(12),
            borderSide: const BorderSide(color: SpeakblendColors.primaryColor)),
      ),
    );
  }
}

class _SpeakBlendButton extends StatelessWidget {
  final String text;
  final IconData? icon;
  final Color backgroundColor;
  final Color textColor;
  final VoidCallback onTap;
  final bool isLoading;

  const _SpeakBlendButton(
      {required this.text,
      this.icon,
      required this.backgroundColor,
      required this.textColor,
      required this.onTap,
      this.isLoading = false});

  @override
  Widget build(BuildContext context) {
    return AnimatedContainer(
      duration: const Duration(milliseconds: 300),
      curve: Curves.easeInOut,
      child: Stack(
        children: [
          GestureDetector(
            onTap: isLoading ? null : onTap,
            child: Container(
              padding: const EdgeInsets.all(2),
              decoration: BoxDecoration(
                gradient: LinearGradient(
                  begin: Alignment.topCenter,
                  end: Alignment.bottomCenter,
                  stops: const [0.1, 0.8],
                  colors: [
                    SpeakblendColors.grayColor.withAlpha(128),
                    backgroundColor
                  ],
                ),
                borderRadius: BorderRadius.circular(24),
              ),
              child: Container(
                height: 56,
                width: double.infinity,
                decoration: BoxDecoration(
                    color: backgroundColor,
                    borderRadius: BorderRadius.circular(22)),
                child: isLoading
                    ? Center(
                        child: SizedBox(
                            height: 24,
                            width: 24,
                            child: CircularProgressIndicator(
                                strokeWidth: 2.5,
                                valueColor:
                                    AlwaysStoppedAnimation<Color>(textColor))))
                    : Row(
                        mainAxisAlignment: MainAxisAlignment.center,
                        children: [
                          if (icon != null) ...[
                            Icon(icon, color: textColor, size: 24),
                            const SizedBox(width: 8)
                          ],
                          Text(text,
                              style: SpeakblendTheme.mediumText(
                                  color: textColor, fontSize: 16)),
                        ],
                      ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}
2
likes
160
points
216
downloads
screenshot

Documentation

API reference

Publisher

verified publisherspeakblend.com

Weekly Downloads

A backend-agnostic smart localization system for Flutter. Version-controlled translations with intelligent caching, fallback chains, and parameterized translations.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

flutter, shared_preferences

More

Packages that depend on smart_localization