Skip to content
Open
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
73 changes: 73 additions & 0 deletions assets/i18n/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
{
"appTitle": "Wave",
"@appTitle": {
"description": "The title of the application"
},
"start_screen": {
"welcome_text": "Welcome!",
"start_button": "Start"
},
"start_connection_screen": {
"create_code_text": "Create a code",
"initiate_text": "If you want to initiate a connection",
"create_button": "Create",
"or_text": "OR",
"paste_code_text": "Paste code from friend",
"connect_text": "If you want to connect to already created peer",
"paste_button": "Paste"
},
"enable_microphone_screen": {
"allow_access_text": "Allow access, please",
"mic_on_button": "Mic on"
},
"copy_code_screen": {
"your_code_text": "This is your two-word pair cod. Copy and send it to your friend",
"check_button": "Check pair",
"wait_text": "Wait your friend to paste the code for button enabling",
"fail_create_code_text": "Failed to create code"
},

"paste_code_screen": {
"paste_code_text": "Copy your friend’s code and paste it to the text input below:",
"connect_button": "Connect"
},

"connection_screen": {
"close_peer_button": "Close peer",
"warn_termination_text": "This leads to the termination of your connection",
"help_text": "This might help: ",
"return_to_prev_step_text": "Return to the previous step and try to pair once again",
"return_button": "Return",
"connected_text": "Connected",
"connecting_text": "Connecting",
"fail_to_connect_text": "Failed to connect",
"disconnected_text": "Disconnected",
"successful_connection_text": "Successful connection!",
"device_to_connect_text": "Waiting other device to connect..",
"device_to_accept_text": "Waiting your friend’s device to accept...",
"device_to_answer_text": "Waiting your friend’s device to answer...",
"failed_text": "Failed!",
"connection_lost_text": "Connection lost!"
},

"main_screen": {
"invalid_text": "Invalid two-word code"
},

"call_screen": {
"you_text": "You",
"peer_text": "Peer",
"encrypted_text": "Your call is end-to-end encrypted",
"current_input_device_text": "Current Input Device",
"current_output_device_text": "Current Output Device",
"default_microphone_text": "Default Microphone",
"default_speaker_text": "Default Speaker",
"settings_text": "Settings",
"leave_call_text": "Leave Call",
"join_call_text": "Join Call",
"connected_text": "Connected",
"call_failed_text": "Call Failed",
"connecting_text": "Connecting...",
"ready_text": "Ready to call"
}
}
73 changes: 73 additions & 0 deletions assets/i18n/ru.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
{
"appTitle": "Wave",
"@appTitle": {
"description": "The title of the application"
},
"start_screen": {
"welcome_text": "Welcome!",
"start_button": "Start"
},
"start_connection_screen": {
"create_code_text": "Create a code",
"initiate_text": "If you want to initiate a connection",
"create_button": "Create",
"or_text": "OR",
"paste_code_text": "Paste code from friend",
"connect_text": "If you want to connect to already created peer",
"paste_button": "Paste"
},
"enable_microphone_screen": {
"allow_access_text": "Allow access, please",
"mic_on_button": "Mic on"
},
"copy_code_screen": {
"your_code_text": "This is your two-word pair cod. Copy and send it to your friend",
"check_button": "Check pair",
"wait_text": "Wait your friend to paste the code for button enabling",
"fail_create_code_text": "Failed to create code"
},

"paste_code_screen": {
"paste_code_text": "Copy your friend’s code and paste it to the text input below:",
"connect_button": "Connect"
},

"connection_screen": {
"close_peer_button": "Close peer",
"warn_termination_text": "This leads to the termination of your connection",
"help_text": "This might help: ",
"return_to_prev_step_text": "Return to the previous step and try to pair once again",
"return_button": "Return",
"connected_text": "Connected",
"connecting_text": "Connecting",
"fail_to_connect_text": "Failed to connect",
"disconnected_text": "Disconnected",
"successful_connection_text": "Successful connection!",
"device_to_connect_text": "Waiting other device to connect..",
"device_to_accept_text": "Waiting your friend’s device to accept...",
"device_to_answer_text": "Waiting your friend’s device to answer...",
"failed_text": "Failed!",
"connection_lost_text": "Connection lost!"
},

"main_screen": {
"invalid_text": "Invalid two-word code"
},

"call_screen": {
"you_text": "You",
"peer_text": "Peer",
"encrypted_text": "Your call is end-to-end encrypted",
"current_input_device_text": "Current Input Device",
"current_output_device_text": "Current Output Device",
"default_microphone_text": "Default Microphone",
"default_speaker_text": "Default Speaker",
"settings_text": "Settings",
"leave_call_text": "Leave Call",
"join_call_text": "Join Call",
"connected_text": "Connected",
"call_failed_text": "Call Failed",
"connecting_text": "Connecting...",
"ready_text": "Ready to call"
}
}
3 changes: 0 additions & 3 deletions l10n.yaml

This file was deleted.

117 changes: 117 additions & 0 deletions lib/src/i18n/localizations.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class AppLocalisationDelegate extends LocalizationsDelegate<AppLocalizations> {
const AppLocalisationDelegate();

@override
bool isSupported(Locale locale) => AppLocalizations.isSupported(locale);

@override
bool shouldReload(LocalizationsDelegate<AppLocalizations> old) => false;

@override
Future<AppLocalizations> load(Locale locale) async {
final loc = AppLocalizations(AppLocalizations.fetchLocale(locale));
await loc.load();
return loc;
}
}

class AppLocalizations {
AppLocalizations(this.locale);
final Locale locale;

late final Map<String, String> _keys;

static const Locale defaultLocale = Locale('en');
static const supportedLocales = <Locale>[Locale('ru'), defaultLocale];
static const supportedLanguageCodes = <String>{'en', 'ru'};

static bool isSupported(Locale locale) =>
supportedLanguageCodes.contains(locale.languageCode);

static Locale fetchLocale(Locale locale) =>
isSupported(locale) ? Locale(locale.languageCode) : defaultLocale;

Future<void> load() async {
// 1) База en
final base = await _loadJsonMap('assets/i18n/en.json');
// 2) Текущий язык (может совпадать с en)
final lang = locale.languageCode;
final overlay = lang == 'en'
? const <String, dynamic>{}
: await _tryLoadJsonMap('assets/i18n/$lang.json');

// merge и сплющивание в плоские ключи "a.b.c"
final merged = <String, dynamic>{}
..addAll(base)
..addAll(overlay);
_keys = _flatten(merged);
}

// ---- API ----

String t(String key, {Map<String, Object?> params = const {}}) {
final raw = _keys[key] ?? key;
if (params.isEmpty) return raw;
return _fillPlaceholders(raw, params);
}

String translate(String key, [Map<String, String>? placeholders]) =>
t(key, params: placeholders ?? const {});

static AppLocalizations of(BuildContext context) =>
Localizations.of<AppLocalizations>(context, AppLocalizations)!;

// ---- helpers ----

Future<Map<String, dynamic>> _loadJsonMap(String path) async {
final raw = await rootBundle.loadString(path);
return (json.decode(raw) as Map<String, dynamic>);
}

Future<Map<String, dynamic>> _tryLoadJsonMap(String path) async {
try {
final raw = await rootBundle.loadString(path);
return (json.decode(raw) as Map<String, dynamic>);
} catch (_) {
return const {};
}
}

Map<String, String> _flatten(Map<String, dynamic> map, {String? prefix}) {
final out = <String, String>{};

map.forEach((k, v) {
// пропускаем служебные ключи (на будущее, под ARB-стиль)
if (k.startsWith('@')) return;

final key = prefix == null ? k : '$prefix.$k';
if (v is Map) {
out.addAll(_flatten(v.cast<String, dynamic>(), prefix: key));
} else if (v is String) {
out[key] = v;
} else if (v != null) {
out[key] = v.toString();
}
});

return out;
}

String _fillPlaceholders(String text, Map<String, Object?> params) {
// поддерживаем как ${name}, так и {name}
return text.replaceAllMapped(RegExp(r'\$\{(\w+)\}|\{(\w+)\}'), (m) {
final key = m.group(1) ?? m.group(2)!;
final val = params[key];
return val?.toString() ?? m.group(0)!;
});
}
}

// syntactic sugar: context.l10n.t('key')
extension L10nX on BuildContext {
AppLocalizations get l10n => AppLocalizations.of(this);
}
6 changes: 0 additions & 6 deletions lib/src/localization/app_en.arb

This file was deleted.

Loading