← Back to Blog

Localize Your Flutter App Name Per Language (Android + iOS)

flutterl10nandroidiosapp-nameinfoplist

Localize Your Flutter App Name Per Language (Android + iOS)

You shipped translations for every screen, but the icon on the home screen still says the English name. That is not a bug in your ARB files, and flutter gen-l10n cannot fix it. The launcher label is a native resource owned by the OS, so you localize it the way a native Android or iOS app would. This is the same question behind flutter/website issue #3998 ("Explain how to localize an app name"), which was closed as obsolete without docs ever landing. Here is the complete recipe for both platforms, plus the iOS trap that makes the English name silently fail.

Why gen-l10n can't touch the app name

Your AppLocalizations class only exists once the Flutter engine is running and a MaterialApp has resolved a Locale. The home screen shows your name before any of that happens. Android's launcher reads android:label from the manifest, and iOS SpringBoard reads CFBundleDisplayName from the bundle. Both are resolved by the operating system against the device language, using the platform's own resource lookup. Dart is never consulted.

So you need two things:

  1. Native string resources for each language (Android strings.xml, iOS InfoPlist.strings).
  2. To accept that the name follows the OS language, not the locale your app picked.

Android: values-xx/strings.xml + android:label

A fresh Flutter project hardcodes the name in android/app/src/main/AndroidManifest.xml:

<application
    android:label="my_app"
    ...>

Replace the literal with a string resource reference:

<application
    android:label="@string/app_name"
    ...>

Then create the default resource. Flutter projects don't ship a strings.xml, so add it yourself at android/app/src/main/res/values/strings.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">Recipe Box</string>
</resources>

Now add one folder per language, using Android's values-<lang> qualifier:

android/app/src/main/res/
  values/strings.xml        # default (fallback)
  values-de/strings.xml
  values-fr/strings.xml
  values-ja/strings.xml

android/app/src/main/res/values-de/strings.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">Rezeptbox</string>
</resources>

Region-specific variants use values-pt-rBR (note the r prefix). Keep the un-qualified values/ folder: if a device language has no match, Android falls back to it, and a missing default is a build error.

If you also set android:label on the <activity> element (older templates did), it overrides the application label for the launcher, so point it at the same resource or remove it.

Run flutter run again. Resource changes need a full rebuild, not a hot restart.

iOS: xx.lproj/InfoPlist.strings + CFBundleLocalizations

iOS reads the home-screen name from CFBundleDisplayName in ios/Runner/Info.plist. To localize it, you add a file called InfoPlist.strings inside a language folder (de.lproj, fr.lproj, ...) and put translated values for the same keys in it. Apple's DTS advice from a 2025 forum thread is to localize CFBundleDisplayName (the key that drives the Home screen) and to also localize CFBundleName so the name matches in system dialogs.

1. Register the languages

Open ios/Runner.xcodeproj in Xcode, select the Runner project, go to the Info tab, and add each language under Localizations. This is the same step the official Flutter i18n docs ask for so the App Store lists your languages, and it writes the .lproj references into project.pbxproj. Files that are not referenced in the Xcode project are not copied into the bundle, so don't skip this.

Also declare the languages in Info.plist so the bundle advertises them:

<key>CFBundleLocalizations</key>
<array>
    <string>en</string>
    <string>de</string>
    <string>fr</string>
    <string>ja</string>
</array>

2. Create InfoPlist.strings per language

In Xcode, right-click the Runner group, choose New File > Strings File, name it exactly InfoPlist.strings, then in the File Inspector click Localize... and tick each language. Xcode creates the folders for you:

ios/Runner/
  en.lproj/InfoPlist.strings
  de.lproj/InfoPlist.strings
  fr.lproj/InfoPlist.strings
  ja.lproj/InfoPlist.strings

ios/Runner/de.lproj/InfoPlist.strings:

"CFBundleDisplayName" = "Rezeptbox";
"CFBundleName" = "Rezeptbox";

ios/Runner/ja.lproj/InfoPlist.strings:

"CFBundleDisplayName" = "レシピボックス";
"CFBundleName" = "レシピボックス";

Leave the original CFBundleDisplayName in Info.plist as the fallback. The .strings values override it only when a matching .lproj exists for the device language.

3. The en vs en-US trap

This is where the English name silently does not apply.

  • Use en.lproj, not en-US.lproj, for your English strings. iOS resolves a device set to "English (US)" to en when there is no region-specific bundle, so en.lproj covers every English device. A region-only en-US.lproj with no plain en.lproj is fragile.
  • Region variants for CFBundleName are broken on the Home screen in iOS 17 and 18. Apple reproduced a case where en-US overrides of CFBundleName showed in system dialogs but not on the Home screen (Feedback FB17026797). Their recommendation is to localize CFBundleDisplayName, which is what the Home screen actually uses.
  • Your test device may be lying to you. If the simulator's Preferred Language Order still lists English above the language you are testing, iOS can keep picking the English bundle. Apple's DTS testing recipe is to set the target language and remove English from the preferred list, because that is how real users' phones are configured.

To confirm the files actually shipped, inspect the built app:

flutter build ios --debug
find build/ios/iphoneos/Runner.app -name 'InfoPlist.strings'

You should see one line per .lproj. If a language is missing here, it was never added to the Xcode target.

The name follows the device, your UI follows Locale

This surprises people the first time: a user picks French inside your app's language picker, the whole UI switches, and the icon still says "Recipe Box". That is correct behaviour.

The launcher and the Home screen are separate processes that render your label using the system language. Your in-app Locale lives in the Flutter widget tree and only affects what AppLocalizations.of(context) returns. Nothing in Flutter can reach back out and rename the icon, and neither platform offers a runtime API to do it.

So the mental model is:

What Controlled by Lives in
Home-screen name Device language strings.xml, InfoPlist.strings
In-app text, dates, plurals Your MaterialApp.locale ARB files via gen-l10n

If your in-app text is the part that is misbehaving (AppBar titles or dialogs staying stale after a language switch), that is a different problem with its own fix: see Flutter language switch only works after restart.

Keeping native and ARB names in sync

You now have the same product name in three places: strings.xml per language, InfoPlist.strings per language, and probably an appTitle key in your ARB files for the in-app title. When marketing renames the app for the Japanese market, all three need to move together.

A practical convention: treat the ARB appTitle as the source of truth and copy from it into the native files during release prep. Managing those ARB files in the FlutterLocalisation ARB editor means the canonical translation for each locale is visible in one table instead of scattered across raw JSON, and the ICU plural validation catches the unrelated but common mistake of dropping a plural category a language needs. Native strings.xml and InfoPlist.strings still have to be edited by hand, but at least you are copying from one verified place.

Checklist

  • Android: android:label="@string/app_name", default values/strings.xml, one values-xx/ per language.
  • iOS: languages added in Xcode's Info tab, CFBundleLocalizations in Info.plist, xx.lproj/InfoPlist.strings with CFBundleDisplayName and CFBundleName.
  • Use en.lproj, not en-US.lproj; test with English removed from preferred languages.
  • Full rebuild after changing native resources.
  • Expect the name to follow the device language and nothing else.

Want the in-app side of your translations in one place while you handle the native files? Try FlutterLocalisation free.