Hello! Today, we'll explore how to implement a translation feature in Flutter using Google Translate API. Let's create a multilingual support system that's both efficient and user-friendly.

1. Required Package Installation

First, add these packages to your pubspec.yaml:

dependencies:
translator: ^1.0.3 # For Google Translate API
language_detector: ^1.0.1 # For language detection
freezed_annotation: 2.4.4 # For state management with freezed
dev_dependencies:
build_runner: ^2.4.12
freezed: 2.4.4

2. Implementing TranslatorService

Create a service class for translation functionality:

import 'package:language_detector/language_detector.dart';
import 'package:translator/translator.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

@Riverpod(keepAlive: true)
class TranslatorService extends _$TranslatorService {
@override
String? build() => null;
Future<String> translate(String text) async {
  final translator = GoogleTranslator();
  final isSameLanguage = await detectSameLanguage(text);
  // Skip translation if same language
  if (isSameLanguage) {
    return text;
  }
  // Translate to user's selected language
  final translation = await translator.translate(text, to: 'en');
    return translation.text;
  }
  Future<bool> detectSameLanguage(String text) async {
    final languageCode = await LanguageDetector.getLanguageCode(content: text);
    return languageCode == 'en'; // For English
  }
}

3. Freezed Model for State Management

Define a model to manage post states:

@freezed
class Post with _$Post {
  const factory Post({
  required String title,
  required String titleTranslated,
  required String content,
  required String contentTranslated,
  required bool isSameLanguage,
  required bool isTranslated,
  }) = _Post;
}

4. Translation Feature Implementation

class PostController extends _$PostController {
  Future<void> changePostLanguage(String postId) async {
    final post = state.value!.posts.firstWhere((post) => post.id == postId);
    
    // Skip if same language
    if (post.isSameLanguage) return;

    // Use cached translation if available
    if (post.isTranslated) {
      final translatedPost = post.copyWith(
        titleTranslated: post.titleTranslated,
        contentTranslated: post.contentTranslated,
        isTranslated: true,
      );
      _updatePost(postId, translatedPost);
      return;
    }

    // Perform new translation
    final translatedTitle = await _translatorService.translate(post.title);
    final translatedContent = await _translatorService.translate(post.content);

    final translatedPost = post.copyWith(
      titleTranslated: translatedTitle,
      contentTranslated: translatedContent,
      isTranslated: true,
    );

    _updatePost(postId, translatedPost);
  }

  // Restore to original language
  Future<void> restorePostLanguage(String postId) async {
    final post = state.value!.posts.firstWhere((post) => post.id == postId);
    
    final restoredPost = post.copyWith(
      titleTranslated: post.title,
      contentTranslated: post.content,
      isTranslated: false,
    );

    _updatePost(postId, restoredPost);
  }
}

5. UI Implementation

class TranslatePostCard extends ConsumerWidget {
  final Post post;
  
  const TranslatePostCard({
    Key? key,
    required this.post,
  }) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Card(
      margin: const EdgeInsets.all(16.0),
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // Title
            Text(
              post.isTranslated ? post.titleTranslated : post.title,
              style: const TextStyle(
                fontSize: 18,
                fontWeight: FontWeight.bold,
              ),
            ),
            const SizedBox(height: 8),
            
            // Content
            Text(
              post.isTranslated ? post.contentTranslated : post.content,
              style: const TextStyle(fontSize: 16),
            ),
            const SizedBox(height: 16),
            
            // Translation button
            if (!post.isSameLanguage)
              Row(
                mainAxisAlignment: MainAxisAlignment.end,
                children: [
                  TranslateButton(
                    isTranslated: post.isTranslated,
                    onPressed: () {
                      if (post.isTranslated) {
                        ref.read(postControllerProvider.notifier)
                           .restorePostLanguage(post.id);
                      } else {
                        ref.read(postControllerProvider.notifier)
                           .changePostLanguage(post.id);
                      }
                    },
                  ),
                ],
              ),
          ],
        ),
      ),
    );
  }
}

6. Benefits of Maintaining Original Content

  • Performance Optimization:
  • Reduces repeated API calls by caching translated content
  • Enables instant switching between original and translated versions
  • User Experience:
  • Quick toggle between original and translated content
  • Allows users to verify translation accuracy
  • Data Integrity:
  • Preserves original content for recovery from translation errors
  • Prevents loss of critical information during translation
  • Cost Efficiency:
  • Minimizes translation API calls
  • Reduces server load through cached translation results
None

Conclusion

This translation implementation provides users with:

  • Intuitive UI for easy translation access
  • Seamless switching between original and translated content
  • Fast response times for better user experience
  • Efficient resource management for stable service

Using Freezed for state management and original content preservation strategy enables a robust and efficient translation feature.