Skip to content

Commit

Permalink
Merge pull request #3 from mineral-dart/develop
Browse files Browse the repository at this point in the history
feat: Release 2.0.0
  • Loading branch information
LeadcodeDev authored Mar 29, 2023
2 parents f74b835 + 726d7f2 commit a28a445
Show file tree
Hide file tree
Showing 12 changed files with 260 additions and 136 deletions.
19 changes: 19 additions & 0 deletions .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: Publish to pub.dev

on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+*' # tag pattern on pub.dev: 'v'
# Publish using custom workflow
jobs:
publish:
permissions:
id-token: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: dart-lang/setup-dart@v1
- name: Install dependencies
run: dart pub get
- name: Publish
run: dart pub publish --force
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 1.0.0
## 2.0.0
- Rewrite package to follow new `mineral: ^3.1.0` version of Core.

## 1.1.0
- Write readme.

## 1.0.0
- Initial version.
73 changes: 72 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,74 @@
# 🌐 I18n

The I18n module allows you to translate your textual content into multiple languages from yaml files while injecting your variables.
The i18n module has been designed exclusively for the Mineral framework, it allows you to translate your textual content through yaml files.

## Register the module

After installing the module, please register it within `./src/main.dart` following the scheme below :
```dart
Future<void> main () async {
final i18n = I18n([Lang.fr, Lang.enGB]);
Kernel kernel = Kernel()
..intents.defined(all: true)
..plugins.use([i18n]);
await kernel.init();
}
```

## Translate your textual content
As a first step, please create a `lang` folder containing `{lang}.yaml` translation files.

We consider the following software structure :
```
lang/
foo/
fr.yaml
en.yaml
```
The files will contain the following keys :
```yaml
# lang/foo/fr.yaml
bar: bar en français !
```
```yaml
# lang/foo/en.yaml
bar: bar in english !
```
Then we can use the `t()` function to translate our key path.

```dart
import 'package:mineral_i18n/mineral_i18n.dart';
class Foo extends MineralEvent<Ready> with Translation {
Future<void> handle (Ready event) async {
final String sentence = t(Lang.fr, 'foo.bar');
print(sentence); // bar en français !
final String sentence = t(Lang.en_GB, 'foo.bar');
print(sentence); // bar in english !
}
}
```

## Injecting variables
The i18n module integrates the possibility of using variables thanks to special characters which will be replaced by the associated variable.

We consider the file `lang/foo/en.yaml` as containing the following key set :
```yaml
bar: {framework} is my favourite framework !
```

Our string is now waiting for a variable named xx which we will give it when we call the `t()` function.
```dart
import 'package:mineral_i18n/mineral_i18n.dart';
class Foo extends MineralEvent<Ready> with Translation {
Future<void> handle (Ready event) async {
final String sentence = t(Lang.en_GB, 'foo.bar', { 'framework': 'Mineral' });
print(sentence); // Mineral is my favourite framework !
}
}
```
3 changes: 2 additions & 1 deletion lib/mineral_i18n.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
library i18n;

export 'src/i18n.dart';
export 'src/lang.dart';
export 'src/contracts/i18n_contract.dart';
export 'src/mixins/translation.dart';
6 changes: 6 additions & 0 deletions lib/src/contracts/i18n_contract.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import 'dart:io';

abstract class I18nContract {
List<String> get languages;
Directory get langPath;
}
81 changes: 28 additions & 53 deletions lib/src/i18n.dart
Original file line number Diff line number Diff line change
@@ -1,49 +1,50 @@
import 'dart:io';

import 'package:mineral_i18n/src/lang.dart';
import 'package:mineral_i18n/src/translation.dart';
import 'package:mineral_ioc/ioc.dart';
import 'package:mineral_contract/mineral_contract.dart';
import 'package:mineral_i18n/src/contracts/i18n_contract.dart';
import 'package:mineral_i18n/src/managers/translation_manager.dart';
import 'package:path/path.dart';
import 'package:yaml/yaml.dart';

class I18n {
final String label = 'I18n';
static String get namespace => 'Mineral/Plugins/I18n';
late final Directory root;
class I18n extends MineralPackageContract implements I18nContract {
TranslationManager translationManager = TranslationManager();

Translation translation = Translation();
final List<Lang> _languages;
final List<String> _languages;
final String folder;

I18n(this._languages, { this.folder = 'lang' });
I18n(this._languages, { this.folder = 'lang' }): super('I18n', 'Official package');

/// ## Languages allowed
/// ```dart
/// final List<Lang> allowedLanguages = i18n.languages;
/// ```
List<Lang> get languages => _languages;
@override
List<String> get languages => _languages;


/// ## Languages root directory
/// ```dart
/// final Directory folder = i18n.langDirectory;
/// final Directory folder = i18n.langPath;
/// ```
Directory get langDirectory => Directory(join(root.path, folder));

/// Insert languages into i18n instance
void registerLanguages() {
for (final Lang lang in _languages) {
translation.cache.putIfAbsent(lang.normalize, () => {});
}
}
@override
Directory get langPath => Directory(join(root.path, folder));

/// Initialize i18n package
@override
Future<void> init () async {
if (!await langDirectory.exists()) {
if (!await langPath.exists()) {
throw Exception('Missing $folder folder');
}

registerLanguages();
_walk(langDirectory);
_walk(langPath);
}

/// Insert languages into i18n instance
void registerLanguages() {
for (final lang in _languages) {
translationManager.cache.putIfAbsent(lang, () => {});
}
}

/// Recursively browses folders to extract translations
Expand All @@ -53,48 +54,22 @@ class I18n {

for (final item in items) {
if (item is Directory) {
translation.cache.putIfAbsent(location, () => {});
translationManager.cache.putIfAbsent(location, () => {});
_walk(item);
}

if (item is File) {
final filename = item.path.split(separator).last.split('.').first;
final content = loadYaml(item.readAsStringSync());

if (translation.cache[filename] is Map) {
if (item.parent.path == langDirectory.path) {
translation.cache[location] = content;
if (translationManager.cache[filename] is Map) {
if (item.parent.path == langPath.path) {
translationManager.cache[location] = content;
} else {
translation.cache[filename].putIfAbsent(location, () => content);
translationManager.cache[filename].putIfAbsent(location, () => content);
}
}
}
}
}
}

/// Translates the sentence defined by the key set into the requested language.
/// Replacement parameters can be injected.
/// ```dart
/// final String sentence = t(Lang.enGB, 'foo.bar');
/// print(sentence) 👈 'Hello {user}'
///
/// final String sentence = t(Lang.enGB, 'foo.bar', { 'user': 'Freeze' });
/// print(sentence) 👈 'Hello Freeze'
/// ```
String t (Lang lang, String key, { Map<String, dynamic>? replacers }) {
final I18n i18n = ioc.singleton(I18n.namespace);
dynamic target = i18n.translation.cache[lang.normalize];

for (final element in key.split('.')) {
target = target[element];
}

if (replacers != null) {
for (final replacer in replacers.entries) {
target = target.toString().replaceAll('{${replacer.key}}', replacer.value);
}
}

return target;
}
43 changes: 0 additions & 43 deletions lib/src/lang.dart

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import 'package:mineral_i18n/src/lang.dart';

class Translation {
class TranslationManager {
final Map<String, dynamic> _cache = {};

Map<String, dynamic> get cache => _cache;

void addTranslations (Lang lang, Map<String, String> translations) {
void addTranslations (final String lang, Map<String, String> translations) {
for (final translation in translations.entries) {
dynamic location = _cache[lang.normalize];
dynamic location = _cache[lang] ?? {};
List<String> keys = translation.key.split('.');

for (final key in keys) {
Expand Down
28 changes: 28 additions & 0 deletions lib/src/mixins/translation.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import 'package:mineral_i18n/mineral_i18n.dart';
import 'package:mineral_ioc/ioc.dart';

mixin Translation {
/// Translates the sentence defined by the key set into the requested language.
/// Replacement parameters can be injected.
/// ```dart
/// final String sentence = t('en', 'foo.bar');
/// print(sentence); 👈 'Hello {user}'
///
/// final String sentence = t('en', 'foo.bar', replacers { 'user': 'Freeze' });
/// print(sentence); 👈 'Hello Freeze'
/// ```
String t (String lang, String key, { Map<String, dynamic>? replacers }) {
dynamic target = ioc.use<I18n>().translationManager.cache[lang];
for (final element in key.split('.')) {
target = target[element];
}

if (replacers != null) {
for (final replacer in replacers.entries) {
target = target.toString().replaceAll('{${replacer.key}}', replacer.value);
}
}

return target;
}
}
Loading

0 comments on commit a28a445

Please sign in to comment.