Jesway / flutter_translate

Flutter Translate is a fully featured localization / internationalization (i18n) library for Flutter.
MIT License
401 stars 118 forks source link

Ability to customise argument escape characters #76

Closed MichaelMarner closed 2 years ago

MichaelMarner commented 3 years ago

This package uses single braces to escape arguments in translation strings:

{
  "message": "Hello {name}!"
}

Most other similar translation libraries (eg ngx-translate, i18next) use double braces to escape:

{
  "message": "Hello {{name}}!"
}

We are using flutter_translate in our Flutter app, and ngx-translate in our Web app. We would really like to consolidate our translation strings so they are shared between both projects, as this will help ensure messages are consistent across platforms and reduce costs of translating to other languages.

I'm wondering if a good approach would be to allow customising the argument escape characters, so we could use double-braces in our project. For backwards compatibility, we would default to the current single brace behaviour. Perhaps set the escape characters as optional parameters to Localization.load:

class Localization
{
    static void load(Map<String, dynamic> translations, {openingEscapeString = '{', closingEscapeString = '}'})
    {
        instance._translations = translations;
        instance._openingEscapeString = openingEscapeString;
        instance._closingEscapeString = closingEscapeString;
    }
}

I'm happy to do the work to support this, would just like some feedback on the approach.

Cheers!

bratan commented 3 years ago

Hi Michael, I suppose we can add double braces as a configurable option, but we have to keep it simple.

I think the best way would be to add a useDoubleBraces property to the localization delegate create method, which can be configured on startup.

We can then pass this value to the localization object through an initializer instead of the load method since the purpose of this method is to only load localizations.

MichaelMarner commented 2 years ago

So the reason I suggested having the escape characters as parameters to load is so the meaning is something like:

"here are these translation strings, which use { and } as escape characters"

In other words, the escape characters are a property of the localisation strings, not a global configuration property on the library, and would allow for loading more translations at a later time, etc.

I'm also not fussed, so I'm happy to make it a parameter to create if that's what you'd prefer. Perhaps though use an enum for the type of escape characters instead of a boolean flag for double would be cleaner though?

Cheers

ekuleshov commented 2 years ago

I have a similar need. The translation file is coming from some translation service and is shared with other native Android apps. So, I need to apply some transformations on the texts, e.g. android localization uses %s in values and other things like html formatting does not work well in Flutter.

I made a local fork of flutter-translate and extended LocalizationDelegate.create(..) that takes post-processors for keys and plurals. I can contribute it in a PR if you'd be interesting to integrate it.

Here is an example of the post processing I need use:

  static String _processString(String value, String key, String arg) {
    return value
        .replaceAll('\\\'', '\'')
        .replaceAll('\\n', '\n')
        .replaceAllMapped(RegExp(r'^\"(.*)\"$'), (m) => m[1]!)
        .replaceAllMapped(RegExp(r'(.*):$'), (m) => m[1]!)
        .replaceAll('<b>', '')
        .replaceAll('</b>', '')
        .replaceAll('\\"', '"')
        .replaceAll('%s', '{s}')
        .replaceAll('%.2f', '{s}')
        .replaceAll('%.4f', '{s}')
        .replaceAll(r'%1$s', '{s1}')
        .replaceAll(r'%2$s', '{s2}')
        .replaceAll(r'%3$s', '{s3}')
        .replaceAll(r'%4$s', '{s4}')
        .replaceAll(key, arg);
  }

  static String _processPlural(String value, String key, String arg) {
    return value
        .replaceAll('%s', '{{value}}')
        .replaceAll(key, arg);
  }