'String xml file in Flutter
In flutter string text are directly set to the TextField
widget like:
new Text('Hello, How are you?')
Is correct way ? or we can maintain all string in one file and use it like:
<string name="name_hint">Hello, How are you?</string>
Is it possible ?
Solution 1:[1]
Flutter currently doesn’t have a dedicated resources-like system for strings. At the moment, the best practice is to hold your copy text in a class as static fields and accessing them from there. For example:
class Strings {
static const String welcomeMessage = "Welcome To Flutter";
}
Then in your code, you can access your strings as such:
Text(Strings.welcomeMessage)
Edit May '19:
There's now this package that allows you to create json files with your Strings. It will allow you to create Strings for plurals, genders and languages etc
You can create a separate json file for each language like so:
string_en.json
{
"thanks": "Thanks."
}
string_nl.json
{
"thanks": "Dankjewel."
}
And then use this to access it
S.of(context).thanks;
It will know which language to choose based on your phone's default language.
Solution 2:[2]
Screenshot:
Full Code (Null safe):
For those of you who don't want to use any 3rd party plugin, here is how you can do it.
Create a folder
strings
inassets
. Put your language file in it.assets strings - en.json // for english - ru.json // for russian
Now in
en.json
, write your string, for example.{ "text1": "Hello", "text2": "World" }
Similarly, in
ru.json
,{ "text1": "??????", "text2": "???" }
Add this to
pubspec.yaml
file (mind the spaces)flutter: uses-material-design: true assets: - assets/strings/en.json - assets/strings/ru.json flutter_localizations: sdk: flutter
Now you are all set to use these strings in your app. Here is the sample code, the
AppBar
shows the translated text.void main() { runApp( MaterialApp( locale: Locale("ru"), // switch between en and ru to see effect localizationsDelegates: [const DemoLocalizationsDelegate()], supportedLocales: [const Locale('en', ''), const Locale('ru', '')], home: HomePage(), ), ); } class HomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(DemoLocalizations.of(context).getText("text2") ?? "Error")), ); } } // this class is used for localizations class DemoLocalizations { static DemoLocalizations? of(BuildContext context) { return Localizations.of<DemoLocalizations>(context, DemoLocalizations); } String getText(String key) => language[key]; } late Map<String, dynamic> language; class DemoLocalizationsDelegate extends LocalizationsDelegate<DemoLocalizations> { const DemoLocalizationsDelegate(); @override bool isSupported(Locale locale) => ['en', 'ru'].contains(locale.languageCode); @override Future<DemoLocalizations> load(Locale locale) async { String string = await rootBundle.loadString("assets/strings/${locale.languageCode}.json"); language = json.decode(string); return SynchronousFuture<DemoLocalizations>(DemoLocalizations()); } @override bool shouldReload(DemoLocalizationsDelegate old) => false; }
Solution 3:[3]
You can use the methods represented in the internationalization sections of the documentation to control both centralized string management and translations (if you need translations)
https://flutter.io/tutorials/internationalization/
It might be overkill for a simple app with only a few strings though.
Solution 4:[4]
I would separate these classes into individual files, but just to explain my approach for this question.
I have a base class which has my strings getters. Every language I want to support I have to create a class which extends from this class and override its getters. Thus, whenever I create a string, I have to override each implementation of this base class. It is helpful to avoid forgetting to create some locale specific string.
/// Interface strings
class Strings {
String get hello;
}
/// English strings
class EnglishStrings extends Strings {
@override
String get hello => 'Hello';
}
/// Russian strings
class RussianStrings extends Strings {
@override
String get hello => '??????';
}
/// Portuguese strings
class PortugueseStrings extends Strings {
@override
String get hello => 'Olá';
}
After that, in a global scope of your application, you could declare a unique instance of the locale you want to use (using a singleton is a good option).
Just showing a short example of using it:
class Resources {
BuildContext _context;
Resources(this._context);
Strings get strings {
// It could be from the user preferences or even from the current locale
Locale locale = Localizations.localeOf(_context);
switch (locale.languageCode) {
case 'pt':
return PortugueseStrings();
case 'ru':
return RussianStrings();
default:
return EnglishStrings();
}
}
static Resources of(BuildContext context){
return Resources(context);
}
}
And finally, using it in some widget:
Text(Resources.of(context).strings.hello)
Using an extension from BuildContext
You can extend BuildContext to create some particular features and give more power to your application. This is available from Dart 2.7. See more.
app_context_extension.dart
extension AppContext on BuildContext {
Resources get resources => Resources.from(this);
}
favorites_page.dart
import 'package:flutter/material.dart';
// you have to import it yourself. The auto import does not work in this case
import 'package:myapp/ui/extensions/app_context_extension.dart';
class FavoritesPage extends StatefulWidget {
@override
_FavoritesPageState createState() => _FavoritesPageState();
}
class _FavoritesPageState extends State<FavoritesPage> {
@override
Widget build(BuildContext context) {
return Text(context.resources.strings.hello);
}
}
Using GlobalKey
Along with an extension of BuildContext as shown above, you can also use GlobalKey. Basically, you could use it when you do not have a context instance. This last one has a good advantage. You could use strings anywhere in your application. In other words, if you use some pattern like MVC for instance and want to use strings in your controllers, you could easily do it.
You can declare something like this:
application.dart
import 'package:myapp/ui/extensions/app_context_extension.dart';
import 'package:myapp/ui/values/resources.dart';
import 'package:flutter/material.dart';
class Application {
static GlobalKey<NavigatorState> navKey = GlobalKey();
static Resources get resources {
return navKey.currentContext.resources;
}
}
main.dart
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: Application.navKey,
...
And then:
import 'package:flutter/material.dart';
import 'package:myapp/application/application.dart';
class FavoritesPage extends StatefulWidget {
@override
_FavoritesPageState createState() => _FavoritesPageState();
}
class _FavoritesPageState extends State<FavoritesPage> {
@override
Widget build(BuildContext context) {
return Text(Application.resources.strings.hello);
}
}
Hope it helps!
Solution 5:[5]
First of all, create a new strings folder in assets
and add your language JSON files.
assets
strings
- en.json
- ar.json
This is your en.json
file
{
"title": "Flutter app"
}
And this is your ar.json
file
{
"title": "????? Flutter"
}
Then, change your pubspec.yaml
file like below.
dependencies:
# your other codes
intl: ^0.17.0
flutter_localizations:
sdk: flutter
# your other codes
flutter:
uses-material-design: true
assets:
- assets/strings/en.json
- assets/strings/ar.json
After that, create AppLocalizations.dart
class
import 'dart:convert';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
class AppLocalizations {
static AppLocalizations of(BuildContext context) {
return Localizations.of<AppLocalizations>(context, AppLocalizations);
}
String getText(String key) => language[key];
}
Map<String, dynamic> language;
class AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> {
const AppLocalizationsDelegate();
@override
bool isSupported(Locale locale) => ['en', 'ar'].contains(locale.languageCode);
@override
Future<AppLocalizations> load(Locale locale) async {
String string = await rootBundle.loadString("assets/strings/${locale.languageCode}.json");
language = json.decode(string);
return SynchronousFuture<AppLocalizations>(AppLocalizations());
}
@override
bool shouldReload(AppLocalizationsDelegate old) => false;
}
Finally in your main.dart
file make the below changes
void main() async {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: AppLocalizations.of(context).getText("title"),
locale: Locale("en"),
localizationsDelegates: [const AppLocalizationsDelegate()],
supportedLocales: [const Locale('en', ''), const Locale('ar', '')],
home: HomeScreen(),
);
}
}
Solution 6:[6]
create "Strings.dart" file and add the below line==>
class Strings
{
static String welcomeScreen="WelCome Page";
static String loadingMessage="Loading Please Wait...!";
}
And then call the file using the below line using the widget
Text(Strings.loadingMessage)
Make sure that the String.dart file has been imported
Solution 7:[7]
I use this method instead of using third party lib. Basically, I create a class that holds those values (string, colors, dimens, etc)
resources.dart
import 'dart:ui';
class ResString{
var data = {
'url' : 'https://facebook.com/',
'welcome' : 'Welcome Home',
};
String get(String key){
return data[key];
}
}
class ResColor{
var data = {
'colorPrimary' : 0xff652A04,
'colorPrimaryDark' : 0xffFFFFFF,
'colorPrimaryLight' : 0xffF6EDDD,
};
Color get(String key){
return Color(data[key]);
}
}
To use it, simply call the get method
main.dart
import 'package:my_app/resources.dart';
...
return Container(
color: ResColor().get('colorPrimary')
);
...
Solution 8:[8]
It's not funny to mange the languages at all, Android Studio have a build-in plugin of Transalte the words and let you mange it easy, so you can see in a table the Key of the word, and the result in every language you just add, manually of course. soon in Flutter I hope!
Solution 9:[9]
I tried some of the solutions suggested in this post, but the one from @Fakhriddin Abdullaev
was the one that worked well for me. But I didn't like the fact that one must always lookup the key of each string in the.json
files.
So I created a strings.dart
:
import 'package:/AppLocalizations.dart';
class Strings {
static String error = "Error Message";
static AppLocalizations? locator = AppLocalizations();
// ignore: non_constant_identifier_names
static String app_name = locator?.getText("app_name") ?? error;
}
When there is an entry in the .json
files like:
{
"app_name" : "My awesome AppName",
}
you just need to call strings.
and the code completion will suggest needed string:
But do not forget to initialize Strings.locator
with the correct context:
Strings.locator = AppLocalizations.of(context);
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | |
Solution 3 | Jason Smith |
Solution 4 | |
Solution 5 | Fakhriddin Abdullaev |
Solution 6 | jebran |
Solution 7 | Irfandi D. Vendy |
Solution 8 | wps |
Solution 9 |