setup-your-mac / Setup-Your-Mac

Setup Your Mac aims to simplify initial device configuration by leveraging swiftDialog and Jamf Pro Policy Custom Events to allow end-users to self-complete Mac setup post-enrollment.
https://snelson.us/sym
MIT License
231 stars 52 forks source link

Multiple Language, Localization Support #118

Open tonyyo11 opened 9 months ago

tonyyo11 commented 9 months ago

Is your feature request related to a problem? Please describe. Many organizations are multi-lingual and have to account for users who speak/read different languages. We are running into an issue where users with their systems set to fr-CA are forced to read SYM prompts written and designed for en-CA or en-US-based users.

Describe the solution you'd like Although it could easily/potentially lead to "bloat", having code at the beginning of SYM to do a language check for localization would be extremely helpful. I'm not a coder, unfortunately, but I would imagine the SYM code would then have to wrap itself within an "en" array and have an option for admins to copy that section and paste it into multiple additional arrays for "fr" and "es". This would allow setting the headers, text, policy details, etc. into specified languages that admins would be required to translate to.

Describe alternatives you've considered Presently looking into making a full copy of SYM and doing manual translation of all user-displayed text. Then scoping SYM-fr to a Smart Group within Jamf for French Speaking Users. My problem with that would be the requirement of a one-time inventory to Jamf prior to SYM starting off. Becomes even more troublesome when accounting for Prestage enrollments.

Additional context Add any other context or screenshots about the feature request here.

dan-snelson commented 9 months ago

Thanks, @tonyyo11!

hooleahn commented 9 months ago

I have a function in my SYM script at Pre-Flight after runAsUser that pretty much copies what erase-install.sh does.

`setLocalizations() {

currentLoggedInUser

loggedInUserDirectory=$(/usr/libexec/PlistBuddy -c 'Print :dsAttrTypeStandard\:NFSHomeDirectory:0' /dev/stdin <<< "$(/usr/bin/dscl -plist /Search -read "/Users/${loggedInUser}" NFSHomeDirectory)")
# detect the user's language
language=$(/usr/libexec/PlistBuddy -c 'print AppleLanguages:0' "/${loggedInUserDirectory}/Library/Preferences/.GlobalPreferences.plist")
if [[ $language = de* ]]; then
    userLanguage="DE"
elif [[ $language = pt* ]]; then
    userLanguage="PT"
elif [[ $language = es* ]]; then
    userLanguage="ES"
else
    userLanguage="EN"
fi

if [[ $debugMode == "verbose" ]]; then
    userLanguage="ES"
fi

updateScriptLog "User language is: $userLanguage"

# Dialogue localizations - power check - title
dialogPowerTitleEN="Waiting for AC Power Connection"
dialogPowerTitlePT="Aguardando conexão de energia CA"
dialogPowerTitleES="Esperando conexión a la red eléctrica"
dialogPowerTitle=dialogPowerTitle${userLanguage}

# Dialogue localizations - power check - description
dialogPowerDescEN="Please connect your computer to power using an AC power adapter.  \n\nThis process requires AC Power or at least 50% battery."
dialogPowerDescPT="Conecte seu computador à energia usando um adaptador de energia AC. \n\nEste processo requer energia AC ou pelo menos 50% da bateria."
dialogPowerDescES="Conecta tu computadora a la corriente eléctrica mediante un adaptador de poder.  \n\nEste proceso requiere conexion electrica o al menos 50% de bateria."
dialogPowerDesc=dialogPowerDesc${userLanguage}

# Dialogue localizations - network type check - title
dialogNetworkTypeTitleEN="Network Connectivity Alert"
dialogNetworkTypeTitlePT="Alerta de conectividade de rede"
dialogNetworkTypeTitleES="Alerta de conexion de red"
dialogNetworkTypeTitle=dialogNetworkTypeTitle${userLanguage}

# Dialogue localizations - network type check - description
dialogNetworkTypeDescEN="Please connect your computer to the network using an Ethernet cable.  \n\nThis will ensure a more stable and faster setup process."
dialogNetworkTypeDescPT="Conecte seu computador à rede usando um cabo Ethernet. \n\nIsso garantirá um processo de configuração mais estável e rápido."
dialogNetworkTypeDescES="Conecta tu computadora a la red por cable Ethernet.  \n\nEsto asegurara un proceso de configuracion mas rapido y estable. \n\nPuedes elegir continuar por Wi-Fi o conecta un cable Ethernet en este momento y apaga el Wi-Fi."
dialogNetworkTypeDesc=dialogNetworkTypeDesc${userLanguage}

# Dialogue localizations - network type check - button
dialogNetworkTypeButtonEN="Continue on Wi-Fi"
dialogNetworkTypeButtonPT="Continuar com Wi-Fi"
dialogNetworkTypeButtonES="Continuar en Wi-Fi"
dialogNetworkTypeButton=dialogNetworkTypeButton${userLanguage}

# Dialogue localizations - network quality check - title
dialogNetworkQualityTitleEN="Network Quality Alert"
dialogNetworkQualityTitlePT="Alerta de qualidade de rede"
dialogNetworkQualityTitleES="Alerta de calidad de red"
dialogNetworkQualityTitle=dialogNetworkQualityTitle${userLanguage}

# Dialogue localizations - network quality check - description
dialogNetworkQualityDescEN="Your network speed was detected to be less than ideal for the setup.\n\n This can impact the setup process and/or cause it to fail.\n\n If you're on Wi-Fi, switch to a different network, to an Ethernet connection or validate the current one.\n\n If you're on Ethernet, switch to a Wi-Fi network or validate the current one."
dialogNetworkQualityDescPT="A velocidade da sua rede foi detectada como inferior à ideal para a configuração.\n\n Isso pode afetar o processo de configuração e/ou causar falhas.\n\n Se estiver em Wi-Fi, mude para uma rede diferente ou aceite a conexão Ethernet atual.\n\n Se estiver em Ethernet, mude para uma rede Wi-Fi ou valide a atual."
dialogNetworkQualityDescES="Se detectó que la velocidad de tu red es inferior a la ideal para la configuración.\n\n Esto puede impactar el proceso de configuracion o causar que falle.\n\n Si estás en Wi-Fi, cambia a una red diferente, a una conexión Ethernet o valida la actual.\n\n Si estás en Ethernet, cambia a una red Wi-Fi o valida la el actual."
dialogNetworkQualityDesc=dialogNetworkQualityDesc${userLanguage}

# Dialogue localizations - network quality check - description
dialogNetworkQualityButtonEN="Continue on Wi-Fi"
dialogNetworkQualityButtonPT="Continuar com Wi-Fi"
dialogNetworkQualityButtonES="Continuar en Wi-Fi"
dialogNetworkQualityButton=dialogNetworkQualityButton${userLanguage}

# Dialogue localizations - Setup Dialog

dialogSetupTitleEN="Setting up ${loggedInUserFirstname}'s Mac"
dialogSetupTitlePT="Configurando o Mac para ${loggedInUserFirstname}"
dialogSetupTitleES="Configurando la Mac de ${loggedInUserFirstname}"
dialogSetupTitle=dialogSetupTitle${userLanguage}

dialogSetupMessageEN="Please wait while the following apps are installed…"
dialogSetupMessagePT="Aguarde enquanto os seguintes aplicativos são instalados…"
dialogSetupMessageES="Por favor espera mientras las siguientes aplicaciones son instaladas…"
dialogSetupMessage=dialogSetupMessage${userLanguage}

dialogSetupInfoboxEN="Analyzing input…"
dialogSetupInfoboxPT="Analisando informacão…"
dialogSetupInfoboxES="Analizando informacion…"
dialogSetupInfobox=dialogSetupInfobox${userLanguage}

dialogSetupInitialProgressEN="Initializing configuration…"
dialogSetupInitialProgressPT="Inicializando configuracão…"
dialogSetupInitialProgressES="Inicializando configuracion…"
dialogSetupInitialProgress=dialogSetupInitialProgress${userLanguage}

dialogSetupInitialButton1TextEN="Wait…"
dialogSetupInitialButton1TextPT="Espere…"
dialogSetupInitialButton1TextES="Espera…"
dialogSetupInitialButton1Text=dialogSetupInitialButton1Text${userLanguage}

dialogSetupDownloadSpeedCheckProgressEN="Checking Network Speed…"
dialogSetupDownloadSpeedCheckProgressPT="Verificando a velocidade da rede…"
dialogSetupDownloadSpeedCheckProgressES="Verificando la velocidad de red…"
dialogSetupDownloadSpeedCheckProgress=dialogSetupDownloadSpeedCheckProgress${userLanguage}

dialogSetupStatusTextPendingEN="Pending…"
dialogSetupStatusTextPendingPT="Pendente…"
dialogSetupStatusTextPendingES="Pendiente…"
dialogSetupStatusTextPending=dialogSetupStatusTextPending${userLanguage}

dialogSetupStatusTextInstalledEN="Installed"
dialogSetupStatusTextInstalledPT="Instalada"
dialogSetupStatusTextInstalledES="Instalada"
dialogSetupStatusTextInstalled=dialogSetupStatusTextInstalled${userLanguage}

dialogSetupStatusTextUpdatedEN="Updated"
dialogSetupStatusTextUpdatedPT="Atualizada"
dialogSetupStatusTextUpdatedES="Actualizada"
dialogSetupStatusTextUpdated=dialogSetupStatusTextUpdated${userLanguage}

dialogSetupStatusTextRunningEN="Running"
dialogSetupStatusTextRunningPT="Rodando"
dialogSetupStatusTextRunningES="Ejecutando"
dialogSetupStatusTextRunning=dialogSetupStatusTextRunning${userLanguage}

dialogSetupStatusTextCheckingEN="Checking"
dialogSetupStatusTextCheckingPT="Verificando"
dialogSetupStatusTextCheckingES="Validando"
dialogSetupStatusTextChecking=dialogSetupStatusTextChecking${userLanguage}

dialogSetupStatusTextUpdatingEN="Updating"
dialogSetupStatusTextUpdatingPT="Atualizando"
dialogSetupStatusTextUpdatingES="Actualizando"
dialogSetupStatusTextUpdating=dialogSetupStatusTextUpdating${userLanguage}

# Dialogue localizations - UserCheck Dialog

dialogUserCheckTitleEN="Erro do usuário"
dialogUserCheckTitlePT="User error"
dialogUserCheckTitleES="Error de Usuario"
dialogUserCheckTitle=dialogUserCheckTitle${userLanguage}

dialogUserCheckMessageEN="The Mac user (*${loggedInUser}*) doesn't match the Okta user (*${jamfConnectUser}*).\n\n Usually, that happens when the user is created manually.\n\n Please contact us at the channel in Slack to resolve the issue."
dialogUserCheckMessagePT="O usuário do Mac (*${loggedInUser}*) não corresponde ao usuário do Okta (*${jamfConnectUser}*).\n\n Normalmente, isso acontece se o usuário foi criado manualmente.\n\n Entre em contato com a equipe de suporte no canal no Slack para resolver esse problema."
dialogUserCheckMessageES="El usuario del Mac (*${loggedInUser}*) no corresponde con el usuario de Okta (*${jamfConnectUser}*).\n\n Usualmente, esto ocurre si el usuario fue creado manualmente.\n\n **Contacta al equipo de soporte en el canal en Slack para solucionar este problema.**"
dialogUserCheckMessage=dialogUserCheckMessage${userLanguage}

}

setLocalizations`

And then when passing the variable to the functions you just use the ! symbol:

title=${!dialogSetupTitle}
message=${!dialogSetupMessage}
bannerImage="/Library/Desktop Pictures/blablabla.png"
brandingBannerDisplayText="true"
helpmessage="If you need assistance, please contact the IT Engineering team [via Jira](https://url.com/blablabla)  \n\n**Computer Information:**  \n- **Operating System:**  ${macOSproductVersion} (${macOSbuildVersion})  \n- **Serial Number:** ${serialNumber}  \n- **Dialog:** ${dialogVersion}  \n- **Started:** ${timestamp}"
infobox=${!dialogSetupInfobox} # Customize at "Update Setup Your Mac's infobox"
initialProgressText=${!dialogSetupInitialProgress}

I would share my complete script, but it's way too customized to be usable. Still, hopefully this provides an starting point.

tonyyo11 commented 8 months ago

I have a function in my SYM script at Pre-Flight after runAsUser that pretty much copies what erase-install.sh does.

Are you going as far as setting the welcomeMessage, the text field prompts for username, etc?

I feel like I'll have to have a massive Preflight section, while the rest of the actual core script is just localization variables. I think I can get that done. Just will take some time lol.

hooleahn commented 6 months ago

@tonyyo11 I don't use a welcomeMessage, we pass department and other user-specific variables from LDAP/Jamf (https://macnotes.wordpress.com/2022/01/14/device-specific-parameters-for-jamf-pro-script-policies/) so the user doesn't have to input any data.

robjschroeder commented 6 months ago

I imagine you could grab the locale from the user, set your country code and then build case statements on your actual messaging accordingly, similar to: https://techitout.xyz/2023/04/12/setup-your-mac-zoom-room-edition/

langleya commented 6 months ago

@hooleahn your post above helped me start some of my customization, appreciate it as we're multi lingual. I can't seem to figure out how to pass a variable into the actual policy triggers so I'd also like to see multi language added into SYM maybe for V2

Editing in as to what I played with this morning was adding this into catch-all portion since I do not give prompts and I was able to have it select the JSON based on this - I haven't tested in pre-stage yet.

` ###

Select "Catch-all" policyJSON

###

Detect the user's language

language=$(/usr/libexec/PlistBuddy -c 'print AppleLanguages:0' "/${loggedInUserDirectory}/Library/Preferences/.GlobalPreferences.plist")

outputLineNumberInVerboseDebugMode
if [[ -n "$presetConfiguration" ]]; then
    symConfiguration="${presetConfiguration}"
elif [[ $language = fr* ]]; then
    symConfiguration="French"
else
    symConfiguration="Catch-all ('Welcome' dialog disabled)"
fi
updateScriptLog "WELCOME DIALOG: Using ${symConfiguration} Configuration …"
policyJSONConfiguration `
hooleahn commented 5 months ago

@hooleahn your post above helped me start some of my customization, appreciate it as we're multi lingual. I can't seem to figure out how to pass a variable into the actual policy triggers so I'd also like to see multi language added into SYM maybe for V2

Editing in as to what I played with this morning was adding this into catch-all portion since I do not give prompts and I was able to have it select the JSON based on this - I haven't tested in pre-stage yet.

` ### # Select "Catch-all" policyJSON ###

Detect the user's language language=$(/usr/libexec/PlistBuddy -c 'print AppleLanguages:0' "/${loggedInUserDirectory}/Library/Preferences/.GlobalPreferences.plist")

outputLineNumberInVerboseDebugMode
if [[ -n "$presetConfiguration" ]]; then
    symConfiguration="${presetConfiguration}"
elif [[ $language = fr* ]]; then
    symConfiguration="French"
else
    symConfiguration="Catch-all ('Welcome' dialog disabled)"
fi
updateScriptLog "WELCOME DIALOG: Using ${symConfiguration} Configuration …"
policyJSONConfiguration `

This line should be something like: elif [[ "$language" == "fr*" ]]; then

dan-snelson commented 1 month ago

Reviewed on 2024-06-07-051826; un-assigned myself; help wanted