flutter_localization_updater

A Flutter package that automatically updates localizations from Google Sheets and integrates seamlessly with easy_localization.

Features

  • 🔄 Automatic Updates: Fetch translations from Google Sheets automatically
  • 📱 Easy Integration: Works with easy_localization out of the box
  • Smart Caching: Configurable update intervals to avoid unnecessary API calls
  • 🌍 Multi-language Support: Support for multiple locales
  • 📁 Dynamic Loading: Load translations from both bundled assets and dynamically updated files

Installation

Add this to your package's pubspec.yaml file:

dependencies:
  flutter_localization_updater: ^1.0.10

Setup

1. Google Sheets API Setup

  1. Go to the Google Cloud Console
  2. Create a new project or select an existing one
  3. Enable the Google Sheets API
  4. Create credentials (API Key)
  5. Create a Google Sheet with your translations

2. Google Sheet Structure

Your Google Sheet should have the following structure:

Key en nl es
general_header_title Welcome Welkom Bienvenido
general_searching_location Searching location... Locatie zoeken... Buscando ubicación...

3. Create Localization Files

Before setting up the localization service, you need to create the initial JSON files for your supported locales. You can do this manually or use the provided command-line tool:

# Create default localization files (en, nl)
dart run flutter_localization_updater:localize

# Create files for specific locales
dart run flutter_localization_updater:localize --locales=en,nl,fr,es

# Specify custom output directory
dart run flutter_localization_updater:localize --output-dir=assets/translations

# Use a custom template file
dart run flutter_localization_updater:localize --template-file=my_template.json

This will create JSON files with translations fetched from your Google Sheets. The files will be created in assets/localizations/ by default (e.g., en.json, nl.json).

Command Options:

  • --locales, -l: Comma-separated list of locale codes (default: en,nl)
  • --output-dir, -o: Output directory for localization files (default: assets/localizations)
  • --template-file, -t: Path to a template JSON file to use as base

Google Sheets Integration: The command automatically fetches translations from your Google Sheets if you set the following environment variables:

  • GOOGLE_SHEETS_API_KEY: Your Google Sheets API key
  • GOOGLE_SHEET_ID: The ID of your Google Sheet (found in the URL)
  • GOOGLE_SHEET_NAME: The name of the specific tab/sheet to use (optional, processes all tabs if not specified)

You can set these variables in several ways:

  1. Using a .env file (recommended):

    # Create a .env file in your project root
    GOOGLE_SHEETS_API_KEY=your_api_key_here
    GOOGLE_SHEET_ID=your_sheet_id_here
    GOOGLE_SHEET_NAME=your_tab_name_here
    
  2. Using system environment variables:

    # On Windows PowerShell:
    $env:GOOGLE_SHEETS_API_KEY="your_api_key_here"
    $env:GOOGLE_SHEET_ID="your_sheet_id_here"
    $env:GOOGLE_SHEET_NAME="your_tab_name_here"
       
    # On Linux/Mac:
    export GOOGLE_SHEETS_API_KEY="your_api_key_here"
    export GOOGLE_SHEET_ID="your_sheet_id_here"
    export GOOGLE_SHEET_NAME="your_tab_name_here"
    

If no specific sheet name is provided, the command will process all available tabs and combine their translations.

Tab Name Prefixing: Property names are automatically prefixed with the tab name and an underscore. Dots in keys are converted to underscores, and all keys are converted to lowercase. For example:

  • Tab name: "Auth"

  • Key in sheet: "LoginButton"

  • Result in JSON: {"auth_loginbutton": "Login"}

  • Tab name: "Auth"

  • Key in sheet: "General.LoginButton"

  • Result in JSON: {"auth_general_loginbutton": "Login"}

  • Tab name: "Profile"

  • Key in sheet: "Settings.Notifications.Email"

  • Result in JSON: {"profile_settings_notifications_email": "Email notifications"}

If these environment variables are not set, the command will create files with template content that you can fill in manually.

Google Sheet Structure: Your Google Sheet should have the following structure:

Key en nl es
general_header_title Welcome Welkom Bienvenido
general_searching_location Searching location... Locatie zoeken... Buscando ubicación...

The first column should contain the translation keys (using dot notation for nested structures), and subsequent columns should contain translations for each locale.

4. Basic Usage

import 'package:flutter/material.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter_localization_updater/flutter_localization_updater.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // Initialize localization service with timeout
  final localizationService = LocalizationService(
    config: LocalizationConfig(
      googleSheetApiKey: 'YOUR_GOOGLE_SHEETS_API_KEY',
      sheetId: 'YOUR_GOOGLE_SHEET_ID',
      supportedLocales: ['en', 'nl', 'es'],
      updateIntervalMs: 24 * 60 * 60 * 1000, // 24 hours
      timeout: Duration(seconds: 10), // 10 seconds timeout
    ),
  );

  // Option 1: Wait for update with timeout (recommended for app startup)
  final success = await localizationService.checkAndUpdateLocalizations();
  if (!success) {
    print('Translation update timed out or failed, continuing with cached data');
  }

  // Option 2: Start background update (use this if you want app to start immediately)
  // localizationService.startBackgroundUpdate();

  // Initialize easy_localization
  await EasyLocalization.ensureInitialized();

  runApp(EasyLocalization(
    supportedLocales: const [Locale('en'), Locale('nl'), Locale('es')],
    path: 'assets/localizations',
    fallbackLocale: const Locale('en'),
    assetLoader: CustomAssetLoader(), // Use the custom asset loader
    child: MyApp(),
  ));
}

API Reference

LocalizationConfig

Configuration class for the localization service.

LocalizationConfig({
  required String googleSheetApiKey,
  required String sheetId,
  String baseUrl = "https://sheets.googleapis.com/v4/spreadsheets/",
  int updateIntervalMs = 24 * 60 * 60 * 1000, // 24 hours
  List<String> supportedLocales = const ['en', 'nl'],
  String lastUpdateKey = 'last_localization_update',
  Duration timeout = const Duration(seconds: 5), // 5 seconds default timeout
})

Parameters:

  • googleSheetApiKey: Your Google Sheets API key
  • sheetId: The ID of your Google Sheet (found in the URL)
  • baseUrl: Base URL for Google Sheets API (usually don't change this)
  • updateIntervalMs: How often to check for updates (in milliseconds)
  • supportedLocales: List of supported locale codes
  • lastUpdateKey: Key for storing last update timestamp
  • timeout: Maximum time to wait for translation updates before continuing

LocalizationService

Main service class for managing localizations.

LocalizationService({
  required LocalizationConfig config,
  Dio? dio,
})

Methods:

  • updateLocalizations(): Force update localizations from Google Sheets (returns Future<bool>)
  • checkAndUpdateLocalizations(): Check if update is needed and update if necessary (returns Future<bool>)
  • startBackgroundUpdate(): Start update in background without blocking the app
  • shouldUpdateLocalizations(): Check if localizations need updating
  • getLocalizationsPath(): Get the path to localizations directory

Timeout and Background Updates

The service now includes timeout functionality to prevent your app from hanging while waiting for translations:

// Configure timeout (default is 5 seconds)
final localizationService = LocalizationService(
  config: LocalizationConfig(
    googleSheetApiKey: 'YOUR_API_KEY',
    sheetId: 'YOUR_SHEET_ID',
    timeout: Duration(seconds: 15), // Custom timeout
  ),
);

// Option 1: Wait for update with timeout (app continues after timeout)
final success = await localizationService.checkAndUpdateLocalizations();
if (!success) {
  print('Translation update timed out or failed, continuing with cached data');
}

// Option 2: Start background update (app continues immediately)
localizationService.startBackgroundUpdate();

Timeout Behavior:

  • If the translation update takes longer than the configured timeout, the app will continue with cached/local translations
  • The update will continue in the background even after timeout
  • Network requests are also timed out to prevent hanging connections
  • Failed updates are logged but don't crash the app

CustomAssetLoader

Custom asset loader for easy_localization that loads from both bundled assets and dynamically updated files.

CustomAssetLoader()

Example

See the example/ directory for a complete working example.

Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

This project is licensed under the MIT License - see the LICENSE file for details.