2025-10-03 00:02:43 -04:00
#if UNITY_ANDROID
using UnityEditor.Callbacks ;
using System.Collections.Generic ;
using System.IO ;
using System.Linq ;
using UnityEditor ;
using UnityEditor.Android ;
using UnityEngine ;
namespace I2.Loc
{
public class PostProcessBuild_Android
#if UNITY_2021_1_OR_NEWER
: IPostGenerateGradleAndroidProject
#endif
{
#if UNITY_2021_1_OR_NEWER
public int callbackOrder = > 0 ;
public void OnPostGenerateGradleAndroidProject ( string path )
{
path = Path . Combine ( path , "src/main" ) ;
PostProcessAndroid ( BuildTarget . Android , path ) ;
}
#else
// Post Process Scene is a hack, because using PostProcessBuild will be called after the APK is generated, and so, I didn't find a way to copy the new files
[PostProcessScene]
public static void OnPostProcessScene ( )
{
// #if UNITY_4_6 || UNITY_4_7 || UNITY_4_8 || UNITY_4_9 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2
// bool isFirstScene = (EditorBuildSettings.scenes.Length>0 && EditorBuildSettings.scenes[0].path == EditorApplication.currentScene);
// #else
// bool isFirstScene = UnityEngine.SceneManagement.SceneManager.GetActiveScene().buildIndex <= 0;
// #endif
//
// if (!EditorApplication.isPlayingOrWillChangePlaymode &&
// (EditorUserBuildSettings.activeBuildTarget == BuildTarget.Android) &&
// isFirstScene)
// {
// string projPath = System.IO.Path.GetFullPath(Application.streamingAssetsPath + "/../../Temp/StagingArea");
// //string projPath = System.IO.Path.GetFullPath(Application.dataPath+ "/Plugins/Android");
// PostProcessAndroid(BuildTarget.Android, projPath);
// }
}
#endif
//[PostProcessBuild(10000)]
public static void PostProcessAndroid ( BuildTarget buildTarget , string pathToBuiltProject )
{
if ( buildTarget ! = BuildTarget . Android )
return ;
if ( LocalizationManager . Sources . Count < = 0 )
LocalizationManager . UpdateSources ( ) ;
// Get language with variants, but also add it without the variant to allow fallbacks (e.g. en-CA also adds en)
2026-03-20 11:56:50 -04:00
var langCodes =
LocalizationManager . GetAllLanguagesCode ( false ) . Concat ( LocalizationManager . GetAllLanguagesCode ( true ) ) . Distinct ( ) . ToList ( ) ;
2025-10-03 00:02:43 -04:00
if ( langCodes . Count < = 0 )
return ;
2026-03-20 11:56:50 -04:00
string stringXML = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
2025-10-03 00:02:43 -04:00
"<resources>\n" +
" <string name=\"app_name\">{0}</string>\n" +
"</resources>" ;
SetStringsFile ( pathToBuiltProject + "/res/values" , "strings.xml" , stringXML , LocalizationManager . GetAppName ( langCodes [ 0 ] ) ) ;
var list = new List < string > ( ) ;
list . Add ( pathToBuiltProject + "/res/values" ) ;
foreach ( var code in langCodes )
{
// Android doesn't use zh-CN or zh-TW, instead it uses: zh-rCN, zh-rTW, zh
string fixedCode = code ;
if ( fixedCode . StartsWith ( "zh" , System . StringComparison . OrdinalIgnoreCase ) )
{
string googleCode = GoogleLanguages . GetGoogleLanguageCode ( fixedCode ) ;
if ( googleCode = = null ) googleCode = fixedCode ;
fixedCode = ( googleCode = = "zh-CN" ) ? "zh-CN" : googleCode ;
}
fixedCode = fixedCode . Replace ( "-" , "-r" ) ;
string dir = pathToBuiltProject + "/res/values-" + fixedCode ;
SetStringsFile ( dir , "strings.xml" , stringXML , LocalizationManager . GetAppName ( code ) ) ;
}
}
static void CreateFileIfNeeded ( string folder , string fileName , string text )
{
try
{
if ( ! System . IO . Directory . Exists ( folder ) )
System . IO . Directory . CreateDirectory ( folder ) ;
if ( ! System . IO . File . Exists ( folder + "/" + fileName ) )
System . IO . File . WriteAllText ( folder + "/" + fileName , text ) ;
}
catch ( System . Exception e )
{
Debug . Log ( e ) ;
}
}
static void SetStringsFile ( string folder , string fileName , string stringXML , string appName )
{
try
{
2026-03-20 11:56:50 -04:00
appName =
appName . Replace ( "&" , "&" ) . Replace ( "<" , "<" ) . Replace ( ">" , ">" ) . Replace ( "\"" , "\\\"" ) . Replace ( "'" , "\\'" ) ;
2025-10-03 00:02:43 -04:00
appName = appName . Replace ( "\r\n" , string . Empty ) . Replace ( "\n" , string . Empty ) . Replace ( "\r" , string . Empty ) ;
if ( ! System . IO . Directory . Exists ( folder ) )
System . IO . Directory . CreateDirectory ( folder ) ;
if ( ! System . IO . File . Exists ( folder + "/" + fileName ) )
{
// create the string file if it doesn't exist
stringXML = string . Format ( stringXML , appName ) ;
}
else
{
stringXML = System . IO . File . ReadAllText ( folder + "/" + fileName ) ;
// find app_name
var pattern = "\"app_name\">(.*)<\\/string>" ;
var regexPattern = new System . Text . RegularExpressions . Regex ( pattern ) ;
if ( regexPattern . IsMatch ( stringXML ) )
{
// Override the AppName if it was found
2026-03-20 11:56:50 -04:00
stringXML =
regexPattern . Replace ( stringXML , string . Format ( "\"app_name\">{0}</string>" , appName ) ) ;
2025-10-03 00:02:43 -04:00
}
else
{
// insert the appName if it wasn't there
int idx = stringXML . IndexOf ( "<resources>" ) ;
if ( idx > 0 )
2026-03-20 11:56:50 -04:00
stringXML =
stringXML . Insert ( idx + "</resources>" . Length , string . Format ( "\n <string name=\"app_name\">{0}</string>\n" , appName ) ) ;
2025-10-03 00:02:43 -04:00
}
}
System . IO . File . WriteAllText ( folder + "/" + fileName , stringXML ) ;
}
catch ( System . Exception e )
{
Debug . Log ( e ) ;
}
}
}
}
#endif