Yaml localizations (and assets codegen)

yalo:loc How to use

1. Localization files

After installing this package you can use .yaml files with localization content. You can place these files in this order

  • assets/**/language_code/intl.yaml
  • assets/**/language_code.intl.yaml
  • assets/**/language_code_intl.yaml

Where language_code is a ua / en / de, etc. Like in example

  • assets/localizations/ua/intl.yaml
  • assets/en/intl.yaml
  • assets/content/de_intl.yaml
  • assets/zh.intl.yaml

2. Describe path to the assets folder

Of course, you must also specify the path to the assets folder in pubspec.yaml

# pubspec.yaml
# ...
flutter:
  # ...
  assets:
    - assets/
  # ...

3. Run the command

When you created all localization files, then run command

flutter pub run yalo:loc

4. Add generated localization dependency to your project

This command will generate a .yalo_locale folder in your project's root directory. Then, add this package as a dependency to your pubspec.yaml file

# ...
dependencies:
  # ...
  yalo_locale:
    path: ./.yalo_locale
  # ...
# ...

5. Use and enjoy

Now you can use generated localization delegate in your app! Yalo provides you to use a simple variable localizationsDelegates, which you can use in your MaterialApp or CupertinoApp. And also, you can use generated variable supportedLocales, which contains all your locales

import 'package:yalo_locale/lib.dart';

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      localizationsDelegates: localizationsDelegates,
      supportedLocales: supportedLocales,
      locale: DevicePreview.locale(context),
      onGenerateTitle: (BuildContext context) =>
      Messages
          .of(context)
          .app
          .title,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      useInheritedMediaQuery: true,
      builder: DevicePreview.appBuilder,
      home: const MyHomePage(),
    );
  }
}

To get access to current localization content, use Messages.of(context). This method will return your current's locale content

Widget build(BuildContext context) {
  final LocalizationMessages loc = Messages.of(context);

  return Scaffold(
    appBar: AppBar(
      title: Text(loc.app.description),
    ),
    body: Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Padding(
            padding: const EdgeInsets.only(bottom: 20),
            child: Text(
              loc.books(user: loc.presidents.getContent('p$_counter'), howMany: _counter),
              textAlign: TextAlign.center,
            ),
          ),
        ],
      ),
    ),
    floatingActionButton: FloatingActionButton(
      onPressed: _incrementCounter,
      tooltip: loc.tooltips.increment,
      child: const Icon(Icons.add),
    ),
  );
}

Also, you can use a static fields of Messages with localized content too. For every language will be generated a static field. For example:

class Messages {
  static LocalizationMessages of(BuildContext context) => Localizations.of(context, LocalizationMessages);

  static LocalizationMessages get de => LocalizationDelegate()._languageMap['de']!;

  static LocalizationMessages get en => LocalizationDelegate()._languageMap['en']!;
}

Localization files format

1. Simple message

# The simplest possible message type [SimpleMessage]
cancel: Cancel
  /// Description: ""
/// Example: "Cancel"
@override
final String cancel = Intl.message(
  '''Cancel''',
  name: 'cancel',
  desc: '',
);

2. Simple message with description

# Simple message type with description [SimpleMessage]
save:
  value: Save
  desc: Saving something
/// Description: "Saving something"
/// Example: "Save"
@override
final String save = Intl.message(
  '''Save''',
  name: 'save',
  desc: 'Saving something',
);

3. Simple message with substitution

# [SimpleMessage] with additional substitution params
greetings: Hello ${userName}! We are glad to see you at our ${hotelName} hotel!
/// Description: ""
/// Example: "Hello $userName! We are glad to see you at our $hotelName hotel!"
@override
String greetings({
  required String userName,
  required String hotelName,
}) =>
    Intl.message(
      '''Hello $userName! We are glad to see you at our $hotelName hotel!''',
      name: 'greetings',
      desc: '',
    );

4. Simple message with description and substitution

# [SimpleMessage] with description and substitution params
weather:
  value: It's ${temperature} degrees outside today!
  desc: "Title on the main weather screen (see here: https://www.figma.com/community/file/974352831498882628)"
/// Description: "Title on the main weather screen (see here: https://www.figma.com/community/file/974352831498882628)"
/// Example: "It's $temperature degrees outside today!"
@override
String weather({
  required String temperature,
}) =>
    Intl.message(
      '''It's $temperature degrees outside today!''',
      name: 'weather',
      desc: 'Title on the main weather screen (see here: https://www.figma.com/community/file/974352831498882628)',
    );

5. Plural message

For additional info about plural rules go here

# [PluralMessage]
book:
  # Required parameter of plural message
  zero: books
  # Required parameter of plural message
  one: book
  # Optional parameter of plural message
  two: books
  # Optional parameter of plural message
  few: books
  # Optional parameter of plural message
  many: books
  # Required parameter of plural message
  other: books
  # Optional parameter of plural message
  desc: Uses for description of books count
/// Description: "Uses for description of books count"
/// Example: "zero: books, one: book, two: books, few: books, many: books, other: books"
@override
String book(int howMany, {int? precision}) =>
    Intl.plural(
      howMany,
      name: '''book''',
      zero: '''books''',
      one: '''book''',
      two: '''books''',
      few: '''books''',
      many: '''books''',
      other: '''books''',
      desc: '''Uses for description of books count''',
      precision: precision,
    );

6. Plural message with using of quantity in the text

# [PluralMessage]
people:
  zero: I saw no people there
  one: I saw only one man there
  two: I saw two people there
  # Message with substitution of quantity of something
  few: I saw ${howMany} people there
  many: I saw ${howMany} or more people there
  other: I think, I saw ${howMany} people there
  desc: An example with numeric substitution in plural strings
/// Description: "An example with numeric substitution in plural strings"
/// Example: "zero: I saw no people there, one: I saw only one man there, two: I saw two people there, few: I saw $howMany people there, many: I saw $howMany or more people there, other: I think, I saw $howMany people there"
@override
String people(int howMany, {int? precision}) =>
    Intl.plural(
      howMany,
      name: '''people''',
      zero: '''I saw no people there''',
      one: '''I saw only one man there''',
      two: '''I saw two people there''',
      few: '''I saw $howMany people there''',
      many: '''I saw $howMany or more people there''',
      other: '''I think, I saw $howMany people there''',
      desc: '''An example with numeric substitution in plural strings''',
      precision: precision,
    );

7. Plural message with extra substitution params

# [PluralMessage] with additional substitution params and quantity substitution param
shop:
  zero: There are no any ${shopName} shops
  one: There are exactly one ${shopName} shop
  two: There are two ${shopName} shops
  few: There are ${howMany} ${shopName} shops and we have plans to open additional at the ${city}!
  many: There are ${howMany} shops. Sorry, I didn't remember that shop name.
  other: Well, I tired to count these shops. I can say that there are only ${howMany} shops!
/// Description: ""
/// Example: "zero: There are no any $shopName shops, one: There are exactly one $shopName shop, two: There are two $shopName shops, few: There are $howMany $shopName shops and we have plans to open additional at the $city!, many: There are $howMany shops. Sorry, I didn't remember that shop name., other: Well, I tired to count these shops. I can say that there are only $howMany shops!"
@override
String shop({
  required String shopName,
  required int howMany,
  required String city,
  int? precision,
}) =>
    Intl.plural(
      howMany,
      name: '''shop''',
      zero: '''There are no any $shopName shops''',
      one: '''There are exactly one $shopName shop''',
      two: '''There are two $shopName shops''',
      few: '''There are $howMany $shopName shops and we have plans to open additional at the $city!''',
      many: '''There are $howMany shops. Sorry, I didn't remember that shop name.''',
      other: '''Well, I tired to count these shops. I can say that there are only $howMany shops!''',
      desc: '''''',
      precision: precision,
    );

8. Namespaced content

# Namespace of group of messages [MessagesNamespace]
namespacedZone:
  # [SimpleMessage]
  title: Title of Namespaced zone
  # [SimpleMessage]
  description: Description of Namespaced zone
  # [MessagesNamespace]
  checkout:
    # [SimpleMessage]
    title: Checkout for additional page
  # [MessagesNamespace]
  innerNamespacedZone:
    # [SimpleMessage]
    title: Deeper title of Inner namespaced zone with name ${name}
    # [SimpleMessage]
    description: Deeper description of Inner namespaced zone
    # [PluralMessage]
    book:
      # Required parameter of plural message
      zero: books
      # Required parameter of plural message
      one: book
      # Optional parameter of plural message
      two: books
      # Optional parameter of plural message
      few: books
      # Optional parameter of plural message
      many: books
      # Required parameter of plural message
      other: books
      # Optional parameter of plural message
      desc: Uses for description of books count
# ...
# [MessagesNamespace]
app:
  # [SimpleMessage]
  title: Yalo app
  # [SimpleMessage]
  description: This app will show you how to use yalo package
/// This is an example of how namespaces works
final String bookTitle = Messages
    .of(context)
    .namespacedZone
    .innerNamespacedZone
    .book(booksQuantity);
final String checkoutTitle = Messages
    .of(context)
    .namespacedZone
    .checkout
    .title;
// ...
final String appTitle = Messages
    .of(context)
    .app
    .title;

It may happen that some block of content will be the same in all languages. In such a case, you can only define this content in one language and link to it in other languages. For example like this:

# en.intl.yaml
presidents:
  p1: George Washington
  p2: John Adams
  p3: Thomas Jefferson
  p4: James Madison
  # ...
# de.intl.yaml
presidents: ~en.presidents

An important note about this feature: at the moment, only first-level (root) elements can be referenced this way.

Dynamic access

In addition to accessing content in a class style, you can also access it dynamically using the T getContent<T>(String key) method of any namespace-class or root LocalizationMessages entity. Thus, you can both move along the chain of class-namespaces, and simply get something dynamically. See here:

mainView:
  firstTab:
    secondStory:
      example:
        title: This is a title
        description: This is a description
---
presidents:
  p1: George Washington
  p2: John Adams
  p3: Thomas Jefferson
  p4: James Madison
  # ...

final String title = loc
    .getContent<MainView>('mainView')
    .getContent<MainViewFirstTab>('firstTab')
    .getContent<FirstTabSecondStory>('secondStory')
    .getContent<SecondStoryExample>('example')
    .getContent('title');

final Widget widget = Padding(
  padding: const EdgeInsets.only(bottom: 20),
  child: Text(
    loc.books(user: loc.presidents.getContent('p$_counter'), howMany: _counter),
    textAlign: TextAlign.center,
  ),
);

Examples

You can see additional examples of generated code, its usage, as well as localization source files in GitHub.


Assets

To generate code with your assets paths, you should run this command:

flutter pub run yalo:asset

This command will generate a .yalo_assets folder in your project's root. Also, as written above, you must add this folder as a dependency of your project:

# ...
dependencies:
  # ...
  yalo_assets:
    path: ./.yalo_assets
  # ...
# ...

After that, you can use Assets class with static and usual fields in your project:

import 'package:yalo_assets/lib.dart';

// MyHomePageState.dart
@override
Widget build(BuildContext context) {
  return Scaffold(
    body: Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          _buildText('Assets example: ${Assets.rigDemoS}'), // <-- this static field contains "assets/rive/Rig Demo.flr2d" string
          _buildText('Assets example: ${Assets().rigDemo}'), // <-- this field contains "assets/rive/Rig Demo.flr2d" string
        ],
      ),
    ),
    floatingActionButton: FloatingActionButton(
      onPressed: _incrementCounter,
      tooltip: 'Increment',
      child: Icon(Icons.add),
    ),
  );
}

Libraries

yalo