Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## [0.1.0] -

* Set Dart 3.8 as minimum constraint
* Ability to set optional placeholder metadata

## [0.0.4] - 20/10/2024

Expand Down
168 changes: 133 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,74 @@
# arb_generator

A dart tool which generates ARB files from CSV files.
A dart tool which generates ARB localization files from CSV files.

## Features

- CSV → ARB conversion
- Flutter intl compatible
- ICU placeholder, plural and select support
- Placeholder metadata support
- Multi-language generation

## Getting Started

### Add dependency

Add the package as a dev dependency:

```yaml
dev_dependencies:
arb_generator:
```

### Define Settings

Next define arb_generator package settings in `pubspec.yaml`:

```yaml
arb_generator:
input_filepath: "assets_dev/test.csv"
output_directory: "lib/l10n"
filename_prepend: "intl_"
csv_settings:
delimiter: ";"
description_index: 1
base_index: 2
```

| Setting | Description |
| ------------------------------- | ------------------------------------------------------------------------------|
| input_filepath | Required. A path to the input CSV file. |
| output_directory | A directory to generate the output ARB file(s). Defaults to `lib/l10n` |
| filename_prepend | Text to prepend to filename of generated files. Defaults to empty string. |
| csv_settings: delimiter | A delimiter to separate columns in the input CSV file. Defaults to `,`. |
| csv_settings: description_index | The description column index. Defaults to `null` (i.e. no description given) |
| csv_settings: base_index | The column index of the base language in the input CSV file. Defaults to `1`. |

### Run package

Run the following command from your project root:

```sh
dart run arb_generator
```

ARB files are then generated in `output_directory`.

## Generated Output

A CSV file of the form

|keys|description|en|de|
|-|-|-|-|
|myKey|The conventional newborn programmer greeting|Hello world!|Hallo Welt!|
|welcome|A welcome message|Welcome {firstName}!|Willkommen {firstName}!|
|numberMessages|An info message about new messages count|{count, plural, zero{You have no new messages} one{You have 1 new message} other{You have {count} new messages}}|{count, plural, zero{Du hast keine neue Nachrichten} one{Du hast eine neue Nachricht} other{Du hast {count} neue Nachrichten}}|
|whoseBook|A message determine whose book it is|{sex, select, male{His book} female{Her book} other{Their book}}|{sex, select, male{Sein Buch} female{Ihr Buch} other{Ihr Buch}}|
|whoseBook|A message determining whose book it is|{sex, select, male{His book} female{Her book} other{Their book}}|{sex, select, male{Sein Buch} female{Ihr Buch} other{Ihr Buch}}|

generates the following ARB files:

is generated into the following ARB file
**en.arb**

```json
{
Expand All @@ -32,55 +87,98 @@ is generated into the following ARB file
},
"whoseBook": "{sex, select, male{His book} female{Her book} other{Their book}}",
"@whoseBook": {
"description": "A message determine whose book it is"
"description": "A message determining whose book it is"
}
}
```

This ARB file can then be converted into localization delegates using [intl](https://docs.flutter.dev/development/accessibility-and-localization/internationalization) or [intl_utils](https://pub.dev/packages/intl_utils).
**de.arb**

### Add dependency
```json
{
"@@locale": "de",
"myKey": "Hallo Welt!",
"@myKey": {
"description": "The conventional newborn programmer greeting"
},
"welcome": "Willkommen {firstName}!",
"@welcome": {
"description": "A welcome message"
},
"numberMessages": "{count, plural, zero{Du hast keine neue Nachrichten} one{Du hast eine neue Nachricht} other{Du hast {count} neue Nachrichten}}",
"@numberMessages": {
"description": "An info message about new messages count"
},
"whoseBook": "{sex, select, male{Sein Buch} female{Ihr Buch} other{Ihr Buch}}",
"@whoseBook": {
"description": "A message determining whose book it is"
}
}
```

Firstly, add the package as a dev dependency:
## CSV Format

```yaml
dev_dependencies:
arb_generator:
```
- The first column must contain translation keys
- Locale columns should use valid locale codes such as `en`, `de` or `en_US`
- The first row is treated as the header row
- ICU message syntax is supported in translation values

### Define Settings
## Flutter Integration

Next define arb_generator package settings in `pubspec.yaml`. Note that `input_filepath` is the only required parameter.
See [example/l10n.yml](example/l10n.yml) and follow the [official documentation](https://docs.flutter.dev/ui/internationalization) to generate localization delegates from the ARB files.

```yaml
arb_generator:
input_filepath: "assets_dev/test.csv"
output_directory: "lib/l10n"
filename_prepend: "intl_"
csv_settings:
delimiter: ";"
description_index: 1
base_index: 2
## Optional Metadata

By default, if a description is given, it is added as metadata in the ARB file:

```json
"welcome": "Welcome {firstName}!",
"@welcome": {
"description": "A welcome message",
}
```

| Setting | Description |
| ------------------------------- | ------------------------------------------------------------------------------|
| input_filepath | Required. A path to the input CSV file. |
| output_directory | A directory to generate the output ARB file(s). Defaults to `lib/l10n` |
| filename_prepend | Text to prepend to filename of generated files. Defaults to empty string. |
| csv_settings: delimiter | A delimiter to separate columns in the input CSV file. Defaults to `,`. |
| csv_settings: description_index | The description column index. Defaults to `null`. |
| csv_settings: base_index | The column index of the base language in the input CSV file. Defaults to `1`. |
Optional placeholder metadata can be supplied by adding a `json` file with the same filename as `input_filepath` input CSV file:

### Run package
```json
{
"@welcome": {
"placeholders": {
"firstName": {
"type": "String",
"example": "Dash"
}
}
}
}
```

Ensure that your current working directory is the project root and run the following command:
This metadata will then be copied into the generated file:

```sh
dart run arb_generator
```json
"welcome": "Welcome {firstName}!",
"@welcome": {
"description": "A welcome message",
"placeholders": {
"firstName": {
"type": "String",
"example": "Dash"
}
}
}
```

ARB files are then generated in `output_directory`.
Now the generated delegates will expect `firstName` to be of type `String` not `Object`.

```dart
/// A welcome message
///
/// In en, this message translates to:
/// **'Welcome {firstName}!'**
String welcome(String firstName);
```

See [example/assets_dev/test.json](example/assets_dev/test.json) for more info.

## Collaboration

Expand Down
2 changes: 1 addition & 1 deletion example/assets_dev/test.csv
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ keys;description;en;de
myKey;The conventional newborn programmer greeting;Hello world!;Hallo Welt!
welcome;A welcome message;Welcome {firstName}!;Willkommen {firstName}!
numberMessages;An info message about new messages count;{count, plural, zero{You have no new messages} one{You have 1 new message} other{You have {count} new messages}};{count, plural, zero{Du hast keine neue Nachrichten} one{Du hast eine neue Nachricht} other{Du hast {count} neue Nachrichten}}
whoseBook;A message determine whose book it is;{sex, select, male{His book} female{Her book} other{Their book}};{sex, select, male{Sein Buch} female{Ihr Buch} other{Ihr Buch}}
whoseBook;A message determining whose book it is;{sex, select, male{His book} female{Her book} other{Their book}};{sex, select, male{Sein Buch} female{Ihr Buch} other{Ihr Buch}}
unreadEmails;How many unread emails for user;{howMany, plural, zero{There are no unread emails for {userName}} one{There is 1 unread email for {userName}} other{There are {howMany} unread emails for {userName}}};{howMany, plural, zero{Es gibt keine ungelesenen Emails für {userName}} one{Es gibt eine ungelesene für {userName}} other{Es gibt {howMany} ungelesenen Emails für {userName}}}
weatherReaction;Reaction to types of weather;{weatherType, select, sunny{Woohoo} cloudy{Meh} rainy{Weeh} other{Other}};{weatherType, select, sunny{Prima} cloudy{In Ordnung} rainy{Mist} other{Other}}
30 changes: 30 additions & 0 deletions example/assets_dev/test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"@welcome": {
"placeholders": {
"firstName": {
"type": "String",
"example": "Dash"
}
}
},
"@numberMessages": {
"placeholders": {
"count": {
"type": "int",
"example": "1"
}
}
},
"unreadEmails": {
"placeholders": {
"howMany": {
"type": "int",
"example": "1"
},
"userName": {
"type": "String",
"example": "dash"
}
}
}
}
30 changes: 26 additions & 4 deletions example/lib/l10n/app_de.arb
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,41 @@
},
"welcome": "Willkommen {firstName}!",
"@welcome": {
"description": "A welcome message"
"description": "A welcome message",
"placeholders": {
"firstName": {
"type": "String",
"example": "Dash"
}
}
},
"numberMessages": "{count, plural, zero{Du hast keine neue Nachrichten} one{Du hast eine neue Nachricht} other{Du hast {count} neue Nachrichten}}",
"@numberMessages": {
"description": "An info message about new messages count"
"description": "An info message about new messages count",
"placeholders": {
"count": {
"type": "int",
"example": "1"
}
}
},
"whoseBook": "{sex, select, male{Sein Buch} female{Ihr Buch} other{Ihr Buch}}",
"@whoseBook": {
"description": "A message determine whose book it is"
"description": "A message determining whose book it is"
},
"unreadEmails": "{howMany, plural, zero{Es gibt keine ungelesenen Emails für {userName}} one{Es gibt eine ungelesene für {userName}} other{Es gibt {howMany} ungelesenen Emails für {userName}}}",
"@unreadEmails": {
"description": "How many unread emails for user"
"description": "How many unread emails for user",
"placeholders": {
"howMany": {
"type": "int",
"example": "1"
},
"userName": {
"type": "String",
"example": "dash"
}
}
},
"weatherReaction": "{weatherType, select, sunny{Prima} cloudy{In Ordnung} rainy{Mist} other{Other}}",
"@weatherReaction": {
Expand Down
30 changes: 26 additions & 4 deletions example/lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,41 @@
},
"welcome": "Welcome {firstName}!",
"@welcome": {
"description": "A welcome message"
"description": "A welcome message",
"placeholders": {
"firstName": {
"type": "String",
"example": "Dash"
}
}
},
"numberMessages": "{count, plural, zero{You have no new messages} one{You have 1 new message} other{You have {count} new messages}}",
"@numberMessages": {
"description": "An info message about new messages count"
"description": "An info message about new messages count",
"placeholders": {
"count": {
"type": "int",
"example": "1"
}
}
},
"whoseBook": "{sex, select, male{His book} female{Her book} other{Their book}}",
"@whoseBook": {
"description": "A message determine whose book it is"
"description": "A message determining whose book it is"
},
"unreadEmails": "{howMany, plural, zero{There are no unread emails for {userName}} one{There is 1 unread email for {userName}} other{There are {howMany} unread emails for {userName}}}",
"@unreadEmails": {
"description": "How many unread emails for user"
"description": "How many unread emails for user",
"placeholders": {
"howMany": {
"type": "int",
"example": "1"
},
"userName": {
"type": "String",
"example": "dash"
}
}
},
"weatherReaction": "{weatherType, select, sunny{Woohoo} cloudy{Meh} rainy{Weeh} other{Other}}",
"@weatherReaction": {
Expand Down
8 changes: 4 additions & 4 deletions example/lib/l10n/app_localizations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -108,15 +108,15 @@ abstract class AppLocalizations {
///
/// In en, this message translates to:
/// **'Welcome {firstName}!'**
String welcome(Object firstName);
String welcome(String firstName);

/// An info message about new messages count
///
/// In en, this message translates to:
/// **'{count, plural, zero{You have no new messages} one{You have 1 new message} other{You have {count} new messages}}'**
String numberMessages(num count);
String numberMessages(int count);

/// A message determine whose book it is
/// A message determining whose book it is
///
/// In en, this message translates to:
/// **'{sex, select, male{His book} female{Her book} other{Their book}}'**
Expand All @@ -126,7 +126,7 @@ abstract class AppLocalizations {
///
/// In en, this message translates to:
/// **'{howMany, plural, zero{There are no unread emails for {userName}} one{There is 1 unread email for {userName}} other{There are {howMany} unread emails for {userName}}}'**
String unreadEmails(num howMany, Object userName);
String unreadEmails(int howMany, String userName);

/// Reaction to types of weather
///
Expand Down
Loading
Loading