Merge pull request #95 from cake-tech/CAKE-272-implement-localization-generation-script

CAKE-272 | implemented localization generation script to the app (gen…
This commit is contained in:
M 2021-03-24 20:39:53 +02:00
commit 4a62e2db33
19 changed files with 349 additions and 8972 deletions

1
.gitignore vendored
View file

@ -95,3 +95,4 @@ vendor/
android/app/.cxx/**
ios/Flutter/.last_build_id
/lib/generated/**

View file

@ -1,8 +1,9 @@
import 'package:cake_wallet/generated/locales.dart';
import 'package:devicelocale/devicelocale.dart';
import 'package:intl/intl.dart';
class LanguageService {
static const Map<String, String> list = {
static const Map<String, String> supportedLocales = {
'en': 'English',
'de': 'Deutsch (German)',
'es': 'Español (Spanish)',
@ -16,6 +17,15 @@ class LanguageService {
'uk': 'Українська (Ukrainian)',
'zh': '中文 (Chinese)'
};
static final list = <String, String> {};
static void loadLocaleList() {
supportedLocales.forEach((key, value) {
if (locales.contains(key)) {
list[key] = value;
}
});
}
static Future<String> localeDetection() async {
var locale = await Devicelocale.currentLocale;

File diff suppressed because it is too large Load diff

View file

@ -1,3 +1,4 @@
import 'package:cake_wallet/entities/language_service.dart';
import 'package:cake_wallet/entities/order.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@ -135,6 +136,7 @@ Future<void> initialSetup(
@required Box<TransactionDescription> transactionDescriptions,
FlutterSecureStorage secureStorage,
int initialMigrationVersion = 13}) async {
LanguageService.loadLocaleList();
await defaultSettingsMigration(
secureStorage: secureStorage,
version: initialMigrationVersion,

View file

@ -5,11 +5,8 @@ import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:provider/provider.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/entities/language_service.dart';
// import 'package:cake_wallet/src/stores/settings/settings_store.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
@ -62,7 +59,5 @@ class LanguageListPage extends BasePage {
});
},
));
return null;
}
}

View file

@ -462,5 +462,10 @@
"fixed_rate_alert" : "Sie können den Empfangsbetrag eingeben, wenn der Festpreismodus aktiviert ist. Möchten Sie in den Festpreismodus wechseln?",
"xlm_extra_info" : "Bitte vergessen Sie nicht, die Memo-ID anzugeben, während Sie die XLM-Transaktion für den Austausch senden",
"xrp_extra_info" : "Bitte vergessen Sie nicht, das Ziel-Tag anzugeben, während Sie die XRP-Transaktion für den Austausch senden"
"xrp_extra_info" : "Bitte vergessen Sie nicht, das Ziel-Tag anzugeben, während Sie die XRP-Transaktion für den Austausch senden",
"exchange_incorrect_current_wallet_for_xmr" : "Wenn Sie XMR von Ihrem Cake Wallet Monero-Guthaben austauschen möchten, wechseln Sie bitte zuerst zu Ihrem Monero Wallet.",
"confirmed" : "Bestätigt",
"unconfirmed" : "Unbestätigt",
"displayable" : "Anzeigebar"
}

View file

@ -462,5 +462,10 @@
"fixed_rate_alert" : "You will be able to enter receive amount when fixed rate mode is checked. Do you want to switch to fixed rate mode?",
"xlm_extra_info" : "Please dont forget to specify the Memo ID while sending the XLM transaction for the exchange",
"xrp_extra_info" : "Please dont forget to specify the Destination Tag while sending the XRP transaction for the exchange"
"xrp_extra_info" : "Please dont forget to specify the Destination Tag while sending the XRP transaction for the exchange",
"exchange_incorrect_current_wallet_for_xmr" : "If you want to exchange XMR from your Cake Wallet Monero balance, please switch to your Monero wallet first.",
"confirmed" : "Confirmed",
"unconfirmed" : "Unconfirmed",
"displayable" : "Displayable"
}

View file

@ -462,5 +462,10 @@
"fixed_rate_alert" : "Podrá ingresar la cantidad recibida cuando el modo de tarifa fija esté marcado. ¿Quieres cambiar al modo de tarifa fija?",
"xlm_extra_info" : "No olvide especificar el ID de nota al enviar la transacción XLM para el intercambio",
"xrp_extra_info" : "No olvide especificar la etiqueta de destino al enviar la transacción XRP para el intercambio"
"xrp_extra_info" : "No olvide especificar la etiqueta de destino al enviar la transacción XRP para el intercambio",
"exchange_incorrect_current_wallet_for_xmr" : "Si desea intercambiar XMR de su saldo de Cake Wallet Monero, primero cambie a su billetera Monero.",
"confirmed" : "Confirmada",
"unconfirmed" : "Inconfirmado",
"displayable" : "Visualizable"
}

View file

@ -462,5 +462,10 @@
"fixed_rate_alert" : "फिक्स्ड रेट मोड की जांच करने पर आप प्राप्त राशि दर्ज कर पाएंगे। क्या आप निश्चित दर मोड पर स्विच करना चाहते हैं?",
"xlm_extra_info" : "एक्सचेंज के लिए XLM ट्रांजेक्शन भेजते समय मेमो आईडी निर्दिष्ट करना न भूलें",
"xrp_extra_info" : "एक्सचेंज के लिए एक्सआरपी लेनदेन भेजते समय कृपया गंतव्य टैग निर्दिष्ट करना न भूलें"
"xrp_extra_info" : "एक्सचेंज के लिए एक्सआरपी लेनदेन भेजते समय कृपया गंतव्य टैग निर्दिष्ट करना न भूलें",
"exchange_incorrect_current_wallet_for_xmr" : "यदि आप अपने केक वॉलेट मोनेरो बैलेंस से एक्सएमआर का आदान-प्रदान करना चाहते हैं, तो कृपया अपने मोनेरो वॉलेट में जाएं।",
"confirmed" : "की पुष्टि की",
"unconfirmed" : "अपुष्ट",
"displayable" : "प्रदर्शन योग्य"
}

View file

@ -89,7 +89,7 @@
"trade_is_powered_by" : "この取引は ${provider}",
"copy_address" : "住所をコピー",
"exchange_result_confirm" : "確認を押すと、送信されます ${fetchingLabel} ${from} と呼ばれるあなたの財布から ${walletName} 下記の住所へ。 または、外部ウォレットから以下のアドレスに送信することもできます/ QRコードに送信できます.\n\n確認を押して続行するか、戻って金額を変更してください.",
"exchange_result_description" : 次のページに示されているアドレスに最低 ${fetchingLabel} ${from} を送信する必要があります。 ${fetchingLabel} ${from} 未満の金額を送信すると、変換されず、返金されない場合があります。",
"exchange_result_description" : "次のページに示されているアドレスに最低 ${fetchingLabel} ${from} を送信する必要があります。 ${fetchingLabel} ${from} 未満の金額を送信すると、変換されず、返金されない場合があります。",
"exchange_result_write_down_ID" : "*上記のIDをコピーまたは書き留めてください.",
"confirm" : "確認する",
"confirm_sending" : "送信を確認",
@ -462,5 +462,10 @@
"fixed_rate_alert" : "固定金利モードにチェックを入れると、受取額を入力できるようになります。 固定金利モードに切り替えますか?",
"xlm_extra_info" : "交換用のXLMトランザクションを送信するときに、メモIDを指定することを忘れないでください",
"xrp_extra_info" : "取引所のXRPトランザクションを送信するときに、宛先タグを指定することを忘れないでください"
"xrp_extra_info" : "取引所のXRPトランザクションを送信するときに、宛先タグを指定することを忘れないでください",
"exchange_incorrect_current_wallet_for_xmr" : "Cake Wallet Moneroの残高からXMRを交換する場合は、最初にMoneroウォレットに切り替えてください。",
"confirmed" : "確認済み",
"unconfirmed" : "未確認",
"displayable" : "表示可能"
}

View file

@ -462,5 +462,10 @@
"fixed_rate_alert" : "고정 금리 모드 체크시 수취 금액 입력이 가능합니다. 고정 속도 모드로 전환 하시겠습니까?",
"xlm_extra_info" : "교환을 위해 XLM 거래를 보낼 때 메모 ID를 지정하는 것을 잊지 마십시오",
"xrp_extra_info" : "교환을 위해 XRP 트랜잭션을 보내는 동안 대상 태그를 지정하는 것을 잊지 마십시오"
"xrp_extra_info" : "교환을 위해 XRP 트랜잭션을 보내는 동안 대상 태그를 지정하는 것을 잊지 마십시오",
"exchange_incorrect_current_wallet_for_xmr" : "Cake Wallet Monero 잔액에서 XMR을 교환하려면 먼저 Monero 지갑으로 전환하십시오.",
"confirmed" : "확인",
"unconfirmed" : "미확인",
"displayable" : "표시 가능"
}

View file

@ -462,5 +462,10 @@
"fixed_rate_alert" : "U kunt het ontvangen bedrag invoeren wanneer de modus voor vaste tarieven is aangevinkt. Wilt u overschakelen naar de vaste-tariefmodus?",
"xlm_extra_info" : "Vergeet niet om de Memo-ID op te geven tijdens het verzenden van de XLM-transactie voor de uitwisseling",
"xrp_extra_info" : "Vergeet niet om de Destination Tag op te geven tijdens het verzenden van de XRP-transactie voor de uitwisseling"
"xrp_extra_info" : "Vergeet niet om de Destination Tag op te geven tijdens het verzenden van de XRP-transactie voor de uitwisseling",
"exchange_incorrect_current_wallet_for_xmr" : "Als u XMR wilt omwisselen van uw Cake Wallet Monero-saldo, moet u eerst overschakelen naar uw Monero-portemonnee.",
"confirmed" : "Bevestigd",
"unconfirmed" : "Niet bevestigd",
"displayable" : "Weer te geven"
}

View file

@ -462,5 +462,10 @@
"fixed_rate_alert" : "Będziesz mógł wprowadzić kwotę otrzymaną, gdy zaznaczony jest tryb stałej stawki. Czy chcesz przejść do trybu stałej stawki?",
"xlm_extra_info" : "Nie zapomnij podać identyfikatora notatki podczas wysyłania transakcji XLM do wymiany",
"xrp_extra_info" : "Nie zapomnij podać tagu docelowego podczas wysyłania transakcji XRP do wymiany"
"xrp_extra_info" : "Nie zapomnij podać tagu docelowego podczas wysyłania transakcji XRP do wymiany",
"exchange_incorrect_current_wallet_for_xmr" : "Jeśli chcesz wymienić XMR z salda Cake Wallet Monero, najpierw przełącz się na portfel Monero.",
"confirmed" : "Potwierdzony",
"unconfirmed" : "Niepotwierdzony",
"displayable" : "Wyświetlane"
}

View file

@ -462,5 +462,10 @@
"fixed_rate_alert" : "Você poderá inserir a quantia recebida quando o modo de taxa fixa estiver marcado. Quer mudar para o modo de taxa fixa?",
"xlm_extra_info" : "Não se esqueça de especificar o Memo ID ao enviar a transação XLM para a troca",
"xrp_extra_info" : "Não se esqueça de especificar a etiqueta de destino ao enviar a transação XRP para a troca"
}
"xrp_extra_info" : "Não se esqueça de especificar a etiqueta de destino ao enviar a transação XRP para a troca",
"exchange_incorrect_current_wallet_for_xmr" : "Se você deseja trocar o XMR de seu saldo da Carteira Monero Cake, troque primeiro para sua carteira Monero.",
"confirmed" : "Confirmada",
"unconfirmed" : "Não confirmado",
"displayable" : "Exibível"
}

View file

@ -462,5 +462,10 @@
"fixed_rate_alert" : "Вы сможете ввести сумму получения тогда, когда будет установлен режим фиксированной ставки. Вы хотите перейти в режим фиксированной ставки?",
"xlm_extra_info" : "Не забудьте указать Memo ID (памятка) при отправке транзакции XLM для обмена",
"xrp_extra_info" : "Не забудьте указать целевой тег при отправке транзакции XRP для обмена"
"xrp_extra_info" : "Не забудьте указать целевой тег при отправке транзакции XRP для обмена",
"exchange_incorrect_current_wallet_for_xmr" : "Если вы хотите обменять XMR со своего баланса Monero в Cake Wallet, сначала переключитесь на свой кошелек Monero.",
"confirmed" : "Подтверждено",
"unconfirmed" : "Неподтвержденный",
"displayable" : "Отображаемый"
}

View file

@ -462,5 +462,10 @@
"fixed_rate_alert" : "Ви зможете ввести суму отримання тоді, коли буде встановлений режим фіксованої ставки. Ви хочете перейти в режим фіксованої ставки?",
"xlm_extra_info" : "Будь ласка, не забудьте вказати ідентифікатор пам'ятки під час надсилання транзакції XLM для обміну",
"xrp_extra_info" : "Будь ласка, не забудьте вказати тег призначення під час надсилання XRP-транзакції для обміну"
"xrp_extra_info" : "Будь ласка, не забудьте вказати тег призначення під час надсилання XRP-транзакції для обміну",
"exchange_incorrect_current_wallet_for_xmr" : "Якщо ви хочете обміняти XMR із вашого балансу Cake Wallet Monero, спочатку перейдіть на свій гаманець Monero.",
"confirmed" : "Підтверджено",
"unconfirmed" : "Непідтверджений",
"displayable" : "Відображуваний"
}

View file

@ -462,5 +462,10 @@
"fixed_rate_alert" : "選中固定費率模式後,您將可以輸入接收金額。 您要切換到固定速率模式嗎?",
"xlm_extra_info" : "發送用於交換的XLM交易時請不要忘記指定備忘錄ID",
"xrp_extra_info" : "發送用於交換的XRP交易時請不要忘記指定目標標記"
"xrp_extra_info" : "發送用於交換的XRP交易時請不要忘記指定目標標記",
"exchange_incorrect_current_wallet_for_xmr" : "如果要从Cake Wallet Monero余额中兑换XMR请先切换到Monero钱包。",
"confirmed" : "已确认",
"unconfirmed" : "未经证实",
"displayable" : "可显示"
}

View file

@ -0,0 +1,149 @@
import 'dart:io';
import 'dart:convert';
import 'localization/localization_constants.dart';
import 'utils/utils.dart';
const inputPath = 'res/values/';
const outputPath = 'lib/generated/';
const localizationFileName = 'i18n.dart';
const localeListFileName = 'locales.dart';
const srcDir = 'srcDir';
const defaultLocale = 'en';
Future<void> main(List<String> args) async {
final extraInfo = args.isNotEmpty ?
args.fold(<String, dynamic>{}, (Map<String, dynamic> acc, String arg) {
final parts = arg.split('=');
var key = normalizeKeyName(parts[0]);
if (key.contains('--')) {
key = key.substring(2);
}
acc[key] = parts.length > 1
? parts[1].isNotEmpty
? parts[1]
: inputPath
: inputPath;
return acc;
})
: <String, dynamic> {srcDir : inputPath};
extraInfo.forEach((key, dynamic value) async {
if (key != srcDir) {
print('Wrong key: $key');
return;
}
final dirPath = value as String;
final dir = Directory(dirPath);
if (!await dir.exists()) {
print('Wrong directory path: $dirPath');
return;
}
final localePath = <String, dynamic>{};
await dir.list(recursive: false).forEach((element) {
try {
final shortLocale = element.path.split('_',)[1].split('.')[0];
localePath[shortLocale] = element.path;
} catch (e) {
print('Wrong file: ${element.path}');
}
});
if (!localePath.keys.contains(defaultLocale)) {
print("Locale list doesn't contain $defaultLocale");
return;
}
try {
var output = '';
var locales = 'const locales = [';
output += part1;
output += textDirectionDeclaration;
var inputContent =
File(localePath[defaultLocale].toString()).readAsStringSync();
var config = json.decode(inputContent) as Map<String, dynamic>;
output += localizedStrings(config: config, hasOverride: false);
output += '}' + '\n\n';
localePath.forEach((key, dynamic value) {
inputContent = File(localePath[key].toString()).readAsStringSync();
config = json.decode(inputContent) as Map<String, dynamic>;
locales += "'$key', ";
output += 'class \$$key extends S {' + '\n';
output += ' const \$$key();' + '\n';
if (key != defaultLocale) {
output += textDirectionDeclaration;
output += localizedStrings(config: config, hasOverride: true);
}
output += '}' + '\n\n';
});
output += classDeclaration;
localePath.keys.forEach((key) {
output += ' Locale("$key", ""),' + '\n';
});
output += part2;
localePath.keys.forEach((key) {
output += ' case "$key":' + '\n';
output += ' S.current = const \$$key();' + '\n';
output += ' return SynchronousFuture<S>(S.current);' + '\n';
});
output += part3;
await File(outputPath + localizationFileName).writeAsString(output);
locales += '];';
await File(outputPath + localeListFileName).writeAsString(locales);
} catch (e) {
print(e.toString());
}
});
}
String localizedStrings({Map<String, dynamic> config, bool hasOverride}) {
var output = '';
final pattern = RegExp('[\$]{(.*?)}');
config.forEach((key, dynamic value) {
final matches = pattern.allMatches(value as String);
if (hasOverride) {
output += ' @override' + '\n';
}
if (matches.isEmpty) {
output += ' String get ${key} => \"\"\"${value}\"\"\";' + '\n';
} else {
final set = matches.map((elem) => elem.group(1)).toSet().toList();
output += ' String ${key}(';
for (var elem in set) {
if (elem == set.last) {
output += 'String ${elem}';
} else {
output += 'String ${elem}, ';
}
}
output += ') => \"\"\"${value}\"\"\";' + '\n';
}
});
return output;
}

View file

@ -0,0 +1,112 @@
const textDirectionDeclaration = """
@override
TextDirection get textDirection => TextDirection.ltr;
""";
const classDeclaration = """
class GeneratedLocalizationsDelegate extends LocalizationsDelegate<S> {
const GeneratedLocalizationsDelegate();
List<Locale> get supportedLocales {
return const <Locale>[
""";
const part1 = """
import \'dart:async\';
import \'package:flutter/foundation.dart\';
import \'package:flutter/material.dart\';
class S implements WidgetsLocalizations {
const S();
static S current;
static const GeneratedLocalizationsDelegate delegate =
GeneratedLocalizationsDelegate();
static S of(BuildContext context) => Localizations.of<S>(context, S);
""";
const part2 = """
];
}
LocaleListResolutionCallback listResolution({Locale fallback, bool withCountry = true}) {
return (List<Locale> locales, Iterable<Locale> supported) {
if (locales == null || locales.isEmpty) {
return fallback ?? supported.first;
} else {
return _resolve(locales.first, fallback, supported, withCountry);
}
};
}
LocaleResolutionCallback resolution({Locale fallback, bool withCountry = true}) {
return (Locale locale, Iterable<Locale> supported) {
return _resolve(locale, fallback, supported, withCountry);
};
}
@override
Future<S> load(Locale locale) {
final String lang = getLang(locale);
if (lang != null) {
switch (lang) {
""";
const part3 = """
default:
}
}
S.current = const S();
return SynchronousFuture<S>(S.current);
}
@override
bool isSupported(Locale locale) => _isSupported(locale, true);
@override
bool shouldReload(GeneratedLocalizationsDelegate old) => false;
Locale _resolve(Locale locale, Locale fallback, Iterable<Locale> supported, bool withCountry) {
if (locale == null || !_isSupported(locale, withCountry)) {
return fallback ?? supported.first;
}
final Locale languageLocale = Locale(locale.languageCode, "");
if (supported.contains(locale)) {
return locale;
} else if (supported.contains(languageLocale)) {
return languageLocale;
} else {
final Locale fallbackLocale = fallback ?? supported.first;
return fallbackLocale;
}
}
bool _isSupported(Locale locale, bool withCountry) {
if (locale != null) {
for (Locale supportedLocale in supportedLocales) {
if (supportedLocale.languageCode != locale.languageCode) {
continue;
}
if (supportedLocale.countryCode == locale.countryCode) {
return true;
}
if (true != withCountry && (supportedLocale.countryCode == null || supportedLocale.countryCode.isEmpty)) {
return true;
}
}
}
return false;
}
}
String getLang(Locale l) => l == null
? null
: l.countryCode != null && l.countryCode.isEmpty
? l.languageCode
: l.toString();
""";