TercyoStorck/internationalization

Added support to named replacement; Some improvements.

Closed this issue · 1 comments

Estou colocando aqui em vez de abrir PR. Espero que ajude!

Como usar:
Strings.of(context).namedOf("key", {'name': value})
Strings.of(context).pluralNamedOf("named_plurals", 2, {'other': "OTHER NUMBER"})

library internationalization;

import 'dart:convert';

import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';

RegExp _namedValuesRegex = RegExp(r"\\?\{[a-zA-Z]+\\?\}");

List<Locale> supportedLocales = [];

class Strings {
  static final Map<String, dynamic> _defaultLocaleStrings = new Map();

  final Locale _locale;
  final Locale _defaultLocale;
  final String _path;

  Map<String, dynamic> _locationStrings;

  Strings._(this._defaultLocale, this._locale, this._path);

  static Strings of(BuildContext context) {
    return Localizations.of<Strings>(context, Strings);
  }

  // TODO: Is there a country code always?
  Future<Map<String, dynamic>> _getFileData(Locale locale) async {
    String path =
        '${this._path}/${locale.languageCode}_${locale.countryCode}.json';
    String data = await rootBundle.loadString(path);
    return json.decode(data);
  }

  _load() async {
    await _loadDefault();

    if (_locale == _defaultLocale) {
      return;
    }

    _locationStrings = await _getFileData(this._locale);
  }

  _loadDefault() async {
    if (_defaultLocaleStrings.isNotEmpty) {
      return;
    }

    Map<String, dynamic> _result = await _getFileData(this._defaultLocale);

    _result.forEach((String key, dynamic value) {
      _defaultLocaleStrings[key] = value;
    });
  }

  bool _hasKey(String key) {
    return (_locationStrings != null && _locationStrings.containsKey(key)) ||
        _defaultLocaleStrings.containsKey(key);
  }

  dynamic _valueOf(String key) {
    if (_locationStrings == null) {
      return _defaultLocaleStrings[key];
    }

    return _locationStrings[key] ?? _defaultLocaleStrings[key];
  }

  String _applyNamedReplacement(String text, Map<String, dynamic> args) {
    Iterable<RegExpMatch> matches = _namedValuesRegex.allMatches(text);
    if (matches.isEmpty) {
      throw Exception("No named replacement found in the text");
    }

    StringBuffer result = StringBuffer();
    int currentTextIndex = 0;
    for (Match match in matches) {
      result.write(text.substring(currentTextIndex, match.start));
      currentTextIndex = match.start;

      String group = match.group(0);
      // Escaped block keys are ignored. Ex: \{texto\}
      if (group.startsWith("\\")) {
        result.write(text.substring(currentTextIndex, match.end));
      } else {
        // Remove block keys
        String key = group.substring(1, group.length - 1);
        dynamic replacement = args[key];
        if (replacement == null) {
          throw Exception("No named replacement for key [$key] was found");
        }
        result.write(replacement.toString());
      }
      currentTextIndex = match.end;
    }

    if (currentTextIndex < text.length - 1) {
      result.write(text.substring(currentTextIndex));
    }

    return result.toString();
  }

  String _getPluralKey(int pluralValue) {
    if (pluralValue > 1) {
      return "other";
    }
    if (pluralValue > 0) {
      return "one";
    }
    return "zero";
  }

  String _getPluralText(String key, int pluralValue) {
    if (!_hasKey(key)) {
      return key;
    }

    Map<String, dynamic> plurals = _valueOf(key);
    String pluralKey = _getPluralKey(pluralValue);
    return plurals[pluralKey].toString();
  }

  String _interpolateValue(String value, List<String> args) {
    if (args == null || args.isEmpty) {
      return value;
    }

    for (int i = 0; i < args.length; i++) {
      value = value.replaceAll("{$i}", args[i]);
    }

    return value;
  }

  String namedOf(String key, Map<String, dynamic> args) {
    if (!_hasKey(key)) {
      return key;
    }

    String value = _valueOf(key).toString();
    if (value.isEmpty || args == null || args.isEmpty) {
      return value;
    }

    return _applyNamedReplacement(value, args);
  }

  String valueOf(String key, {List<String> args}) {
    if (!_hasKey(key)) {
      return key;
    }

    String value = _valueOf(key).toString();

    return _interpolateValue(value, args);
  }

  String pluralOf(String key, int pluralValue, {List<String> args}) {
    String value = _getPluralText(key, pluralValue);
    if (value == key) {
      return key;
    }
    return _interpolateValue(value, args);
  }

  String pluralNamedOf(String key, int pluralValue, Map<String, dynamic> args) {
    String value = _getPluralText(key, pluralValue);
    if (value == key) {
      return key;
    }
    return _applyNamedReplacement(value, args);
  }
}

class InternationalizationDelegate extends LocalizationsDelegate<Strings> {
  final Locale defaultLocale;
  final String path;

  InternationalizationDelegate({
    this.defaultLocale,
    this.path,
  }) : assert(defaultLocale != null && path != null);

  @override
  bool isSupported(Locale locale) => supportedLocales
      .any((value) => value.languageCode == locale.languageCode);

  @override
  Future<Strings> load(Locale locale) async {
    Strings strings = Strings._(this.defaultLocale, locale, this.path);
    await strings._load();
    return strings;
  }

  @override
  bool shouldReload(InternationalizationDelegate old) => false;
}

Now it's supported. Thank you!!